
On 21 February 2026, pragma-code.de was an empty Astro project. Today, Google Search Console reports 45,443 impressions for the domain over 28 days. This article is not a success story – it is a field report: every measure with a date, every number pulled from the Search Console API, and the failures that cost us deploys, rankings and a fair amount of patience.
This article is an in-depth expert contribution from our content cluster. Discover the complete overview on our main page:Technical SEO Audit →
- The result: 101,478 impressions and 580 clicks between 1 March and 11 August 2026 – from a project that started on 21 February 2026 as an empty Astro repository. All figures come from the Search Console API with
dataState: final. - Content alone was not the lever: The curve bends upwards when the performance rebuild, English slugs with correct hreflang and active indexing coincided – not when the hundredth article went live.
- The honest part: A failed deploy caused by an invented icon name, literal asterisks on 16 pages, 47 internal 404 links, and a local rsync that reverted a freshly deployed page to a 404. Every one of these incidents left behind a rule.
There is no shortage of articles about building visibility. Very few of them disclose their own numbers, and fewer still name the measures with a date attached. This one does both – for the simple reason that we run the domain in question ourselves: pragma-code.de.
The starting point is unremarkable. On 21 February 2026, the git log records the first commit: “Initial commit from Astro”. An empty scaffold. In the first 28 days after going live, Google Search Console reports exactly 1,531 impressions and 9 clicks across 57 search queries. In practice, that is zero. Nobody finds this website.
Five and a half months later, in the 28 days up to 11 August 2026, the same property reports 45,443 impressions, 285 clicks and 2,476 distinct search queries. 586 individual URLs appear in search results at least once. That is almost thirty times the impressions and forty-three times the number of queries for which the domain shows up at all.
Figure: Daily clicks and impressions from Google Search Console (API, dataState: final). The dashed lines mark the three rebuilds that can be dated precisely from the git log.
What matters about this curve is not that it rises. It is where it bends. The climb does not start with the first blog article, nor with the fiftieth. It starts in mid-June – and at that point three rebuilds coincided, each of which looked unspectacular on its own.
- The starting point: 625 pages nobody sees
- Foundation: performance is not a bonus, it is the entry ticket
- Technical SEO: the invisible blockers
- i18n: why German slugs under /en cost rankings
- GEO: findability for machines that never click
- The content machine and the orphan problem
- Indexing as a discipline of its own
- What went wrong – the uncomfortable part
- The result in numbers
- What transfers to your project
The starting point: 625 pages nobody sees
The website is a statically generated Astro site, bilingual German and English, now comprising around 625 static pages: service pages, city landing pages, a glossary with more than 530 technical terms, and currently 126 blog articles per language.
That number is the first important point. Many projects fail not because they have too little content, but because they have a lot of content that is technically undiscoverable or disconnected from itself. A static generator produces HTML in arbitrary quantity – but whether Google crawls, indexes and serves those pages for relevant queries depends on entirely different things.
Through March and April the curve sat essentially on the zero line, even though dozens of articles were already online. Content, therefore, was not the problem.
The mistake almost every project starts with
"We need more content." For the first three months, content was the only thing we produced – and the curve stayed flat. What was missing was the technical foundation that would let Google reach and classify that content in the first place. Content without findability is an archive, not a marketing channel.
Foundation: performance is not a bonus, it is the entry ticket
Core Web Vitals are a weak direct ranking factor – that much is true. As an indirect factor they are brutal: a slow site gets crawled less often, produces worse user signals, and loses to otherwise equivalent competitors. The performance rebuild was therefore the first major construction site.
Chatbot out of the critical path (5 June)
The AI chatbot loaded its JavaScript and CSS inside the critical rendering path. Both were decoupled: logic lazy-loaded, the window CSS extracted from the render-blocking components.css, and the chunk preload pushed behind the load event.
Testimonials and i18n out of the bundle (6 June)
The slider and the language logic sat in the critical JS bundle of every single page – including pages with no slider. Both were extracted and are now loaded on demand.
Fonts and icons (29 June)
Manrope was switched to a variable font and a redundant preload of the Phosphor icon font removed. The icon font itself is now built as a subset – only the glyphs actually used ship to the browser.
Layout stability (16 June)
All blog hero images received explicit width and height attributes against Cumulative Layout Shift; five particularly large images were recompressed.
The second wave (28 July and 11 August)
Render-blocking CSS reduced by 28 percent, image weight by 53 percent, footer CSS inlined. In early August, the homepage image payload before LCP was cut from 1,145 KB to roughly 460 KB.
The most important effect of this work is not the Lighthouse score. It is the crawl budget: a Googlebot that fetches a page in 300 instead of 1,200 milliseconds fetches four times as many pages in the same window. At 625 URLs, that is the difference between “fully covered in two weeks” and “in three months”.
Technical SEO: the invisible blockers
The classic technical work ran in parallel. The timeline below is not a best-practice list from a textbook but the actual order in which the problems surfaced – mostly via Search Console, partly via weekly automated audits.
Seven articles carried no BlogPosting schema at all. Since then the shared template generates the Schema.org markup from the frontmatter – hand-written schemas can no longer drift apart.
Core page titles shortened to a maximum of 60 characters, city descriptions extended from stubs to 120–160 characters. Along the way, a double-escaped & visible in snippets came to light.
The organisation was marked up as LocalBusiness even though there is no storefront customers can visit. Changed to Organization with areaServed for the DACH region – more honest, and free of contradictions for Google.
Individual URLs appeared in several sitemap chunks simultaneously. Every URL now appears in exactly one chunk.
Two pages competed for the same search intent around "n8n GDPR". The overlap was resolved: one page sharpened on the query, the other clearly delimited. At the same time, titles and descriptions of all 18 city templates were rewritten – previously they differed only in the city name.
An audit found 47 internal links pointing nowhere – mostly leftovers from the slug migration. All fixed, plus a check added to the build that now catches such links at build time instead of in the next audit.
Expert tip: cannibalization costs more than missing content
Keyword cannibalization rarely shows up in analytics tools, because both pages do "rank" – just both at position 14. The fastest test: filter a query in Search Console and check whether Google alternates between two URLs over time. That alternation is the symptom.
i18n: why German slugs under /en cost rankings
In hindsight, the rebuild of 7 June is the most underrated intervention of all. Before it, the English tree lived under German slugs: /en/kontakt, /en/leistungen. Technically that worked. For search engines it was a broken signal: an English-language page under a German URL matches neither the content’s language nor the user’s expectation, and the URL is one of the few signals Google can evaluate before rendering the page.
Comparison: organically grown i18n vs. a central slug map
- URLs: German slugs under
/en/– a broken signal for language and user expectation - hreflang: assembled by hand in the layout, drifting with every new page
- Language switcher: its own logic in the client script, unaware of some pairs
- Redirects: partly in the config, partly in
.htaccess– contradictory
- URLs: real English slugs –
/en/contact,/en/services - hreflang: generated from the same map, structurally unable to diverge
- Language switcher: reads the same map, knows every pair automatically
- Redirects: real 301s, generated from the map into
.htaccessby script
Since then there is a single file holding every slug pair. It feeds the language links, hreflang, the canonicals, the switcher and the generated redirects. A new bilingual article means: add one line, run the generator, done.
One detail from that migration earned its own rule: a slug pair where the German and English versions are identical must not go into the map – otherwise the generator produces a redirect from /en/blog-x to /en/blog-x. An infinite loop that makes the page unreachable for crawlers.
GEO: findability for machines that never click
A growing share of searches now ends in an AI answer rather than a click. Generative Engine Optimization is therefore not a successor to SEO but a second audience: systems that read, summarise and cite content without a human ever opening the page. Those systems reward different things than a classic ranking algorithm does.
llms.txt generated at build time
Since 8 June the build generates llms.txt and llms-full.txt from the content collection. The decisive part is not the file but the automation: a hand-maintained list is guaranteed to drift from the real inventory, and an outdated index is worse than none.
Direct answer blocks (2 August)
Every article can now carry a compact 40–60 word answer in its frontmatter. It renders visibly above the text and simultaneously feeds abstract and speakable in the schema – a format generative systems can lift directly.
The glossary as an entity hub
More than 530 technical terms with stable anchors. Every technical term in body copy links there. This creates a dense web of term-definition relationships – exactly the structure from which language models derive topical authority.
FAQPage and HowTo schema
Wherever an article contains a glossary with at least three entries or a numbered roadmap, it also gets the matching structured schema. That makes the answer structure machine-readable instead of hiding it in prose.
On 2 August one more measure arrived – the one with the biggest short-term payoff: the striking distance analysis. All queries where a page ranks between position 11 and 20 are pulled from Search Console. Those pages already have the foundation; they lack the final nudge. Instead of writing new articles, internal links were placed deliberately onto exactly those pages.
Why striking distance is the best use of your time
Moving a page from position 40 to position 10 is months of work. Moving a page from position 12 to position 8 is often a matter of three internal links and a sharpened title – and the difference in actual traffic is many times larger, because click-through rate between page two and page one does not scale linearly but jumps.
The content machine and the orphan problem
In July the blog articles were migrated from individual page files to a content collection: every article is now a Markdown file with schema-validated frontmatter, and a shared template renders all the surrounding markup. The gain is less about writing speed than about making certain errors impossible: an article without a category, without a date or with a missing image breaks the build instead of quietly going live broken.
The real problem was a different one. A new article was reachable from exactly one page – the blog index. That makes it an orphan page: present in the sitemap, but without internal link equity.
-
Outgoing links are not enough
Every article points to three thematically related posts. That helps the reader – it does nothing at all for the new article itself, because link equity flows the other way.
-
Reciprocity as an obligation
With every new article, it is additionally registered as a related post in at least two existing articles per language – preferably in ones that themselves have few incoming links.
-
Body copy beats the related block
Where it fits the subject matter, an additional link goes into the middle of an existing article's text. An in-text link with meaningful anchor text carries considerably more weight than a card in the page footer.
-
Measure instead of hoping
A report script lists every article with fewer than two incoming links. A new slug must no longer appear there once the links are in place. That is a check before the push, not a good feeling.
Indexing as a discipline of its own
The point most projects underestimate, in our experience: between “published” and “in Google” lies a step you can actively take. Passively, you wait for the next crawl – which on a young domain with a weak crawl budget takes weeks.
After every deploy, a script submits the URLs that actually changed via the Google Indexing API. It derives those URLs itself from the git diff of the most recent commits – not from a maintained list that forgets what really changed. For Bing, an IndexNow notification runs in parallel.
The expensive misconception
We run five service accounts and expected
1,000 URLs/dayWrong. The quota applies per Google Cloud project, not per service account. Two of our bots sit in the same project and share one.
The reality
Four projects at roughly 200 requests each
800 URLs/daySounds like a detail. On a deploy that changes 222 pages – as happened on 8 August – it decides whether a campaign clears in one day or three.
A second trap nearly cost us a rebuild: service accounts freshly authorised in Search Console kept returning 403 Failed to verify the URL ownership for several minutes. The obvious reaction – rewriting the configuration – would have been wrong. The authorisation simply needs time to propagate.
What went wrong – the uncomfortable part
A field report without field damage is advertising. Each of these five incidents left a fixed rule behind in the project.
Literal asterisks on 16 pages
Markdown is not parsed inside raw HTML blocks. While converting text lists into visual components, prose containing **bold** moved into <p> tags – and then went live with literal asterisks on 16 blog pages. Rule since then: when moving text into raw HTML, the markup is rewritten with it, and the build checks for leftover asterisks.
23 articles with invalid lists
A roadmap component used <li> elements inside a <div> rather than a list. Visually identical, broken for screen readers – and two critical errors across 23 pages in the accessibility audit. Rule since then: the container is an <ol>, and a pre-commit check counts the violations.
The failed deploy caused by an invented icon name
An icon name that does not exist in the catalogue. The build passed locally, because the fast command skips the icon check – in CI, that exact step aborted. Rule since then: before every push, we run exactly the command CI runs. "Works on my machine" without an identical build is not a statement.
Two lockfiles, one aborted deploy
A security bump on a dependency was applied in only one of two places. The resolved version was correct; only the recorded version range diverged – CI aborted with a lockfile conflict and took an unrelated commit down with it. Rule since then: whoever touches the dependency overrides updates both lockfiles.
The local deploy that turned a page into a 404
The worst incident. A local deploy script reported success even when the transfer failed, and never reconciled its state against the main branch. On 25 July it overwrote a fresh deploy with a stale local state – a page published minutes earlier was a 404 again. Rule since then: there is no local deploy path any more. CI only, followed by a live check against the origin.
The common denominator across these five cases is striking: not one of them was an SEO mistake. They were process mistakes. And every one of them cost visibility for days or weeks – either because a page was unreachable, or because it was served broken.
The result in numbers
All values below come from the Search Console API for the domain property sc-domain:pragma-code.de with dataState: final. The comparison sets the first 28 days after launch against the last 28 days up to 11 August 2026.
Impressions
1,531 → 45,443 over 28 days. Almost thirty-fold. Across the entire period: 101,478.
Clicks
9 → 285 over 28 days, roughly thirty-two-fold. Full period: 580 clicks.
Search queries
57 → 2,476 distinct queries with at least one impression. The single most meaningful number here.
Visible pages
586 individual URLs appeared in search results at least once during the last 28 days.
Two honest caveats belong with this. First: the average position is 22.5 – that is page two to three. This domain has gained visibility, but the harvest in the form of clicks is still largely ahead of it. The click-through rate of 0.6 percent is exactly what those positions produce.
Second: the metric we value most is not impressions but the 2,476 distinct search queries. Impressions can come from a single article that goes viral. Being visible for thousands of different queries means Google can classify the domain topically and trusts it to be relevant across a broad field. That is the foundation on which positions grow – and the reason the click curve in the chart follows the impression curve with a delay.
What transfers to your project
The specific measures are tied to our stack. The order is not.
Foundation first, then content. Three months of content on a technically blocked site moved nothing for us. The same content on a clean foundation turned the curve. If you must do both in parallel, prioritise the foundation.
Automate whatever can drift. hreflang, sitemaps, structured data, redirects, llms.txt – anything maintained by hand will eventually diverge from the real inventory. Generate it from one source.
Indexing is an active step. Publishing is not the same as being indexed. Going through the Indexing API shortens weeks to hours on young domains.
Harvest what is almost ripe. The striking distance analysis returns more per hour invested than any new article – as long as pages sit in positions 11 to 20.
Every incident becomes a rule. The real progress in this project lies not in the measures but in the checks that grew out of the mistakes and now catch the same class of error before it goes live.
Quick check: your path to visibility
If you want to know which of these points are open on your own domain, a technical SEO audit is the fastest entry point. How priorities shift once a large share of searches ends in AI answers is covered in the master plan for GEO over SEO. And anyone running many structurally identical pages – locations, categories, use cases – will find the pitfalls in the guide to programmatic SEO.
Do you have questions about visibility, SEO and GEO?
Book a free initial consultationOur Regional Expertise
We are your digital partner – regionally anchored and successfully scaling across borders.
Have a vision?
Let's check together how we can make your idea take flight.
Book your free strategy call nowExtended Specialized Glossary
hreflang
An HTML or HTTP header attribute that tells search engines which language and regional variant of a page is intended for which audience. Incorrect or non-reciprocal hreflang annotations cause Google to serve the wrong language version or to treat variants as duplicates.
Striking Distance
Search queries for which a page already ranks in positions 11 to 20, just outside the first results page. Because the foundation is already in place, targeted internal links and text additions on these pages generate visibility far faster than new content does.
Orphan Page
A page that receives few or no internal links from the rest of the site. It is discoverable via the sitemap, but gains almost no internal link equity and is crawled less frequently and refreshed more slowly.
Google Indexing API
An interface for actively submitting new or changed URLs to Google for indexing instead of waiting for the next crawl. The daily quota of roughly 200 requests applies per Google Cloud project, not per service account.
IndexNow
An open notification protocol that lets websites report changed URLs to participating search engines such as Bing and Yandex. A single notification is forwarded to all participating engines.
Keyword Cannibalization
The situation in which several of your own pages are optimised for the same search intent and therefore compete with one another. Google splits relevance signals across the candidates, alternates the ranking URL, and none of the pages reaches the position a single consolidated page would achieve.


