A fictional business. The figures are a modelled scenario, not a client result.
Ferrovane · Observability & logging platform · Berlin
How an observability platform made 6,000 JavaScript-rendered docs pages visible to Google
+341%indexed docs pages in 8 months · Signal
Time/div
Volts/div
Focus
CH1 · INDEXED DOCS
SWEEP = 8 MONTHS
TRIG · ARMED
Input signal · the challenge
Six thousand pages of docs, and Google could read almost none of them.
Ferrovane's documentation was a client-side SPA, so Google indexed less than a fifth of it and developers landed on competitors' guides instead.
Ferrovane’s docs were its best acquisition channel on paper: 6,040 guides, collector recipes and API references written by the engineers who built the product. But the site was a client-rendered single-page app. Googlebot fetched an empty shell, queued it for rendering, and often gave up before the content loaded. Developers searching for exact error messages and config snippets found competitors’ tutorials, many of them explaining how to wire up Ferrovane.
18.5%of docs URLs indexed at the start (1,120 of 6,040).
We read 30 days of server logs, compared raw and rendered HTML for every URL, and crawled the site with JavaScript off. The faults fell into four bands: rendering, architecture, crawl budget and international.
E-101CRITRendering
Docs shipped as an empty <div id="root"> plus a 1.9 MB bundle. Googlebot's render queue reached most pages days late, and timed out on API-reference pages that fetched content after hydration.
> Raw-HTML word count: 0 on 5,880 of 6,040 URLs
E-102CRITRendering
Navigation was built from onClick handlers on <span> elements, so the crawler found no <a href> links between sections. Most guides were orphans.
> Internal links in raw HTML: 3 per page on average
E-207MAJArchitecture
Six release versions (v3.0 to v3.5) published under different paths with no canonicals. Google kept choosing old v3.1 pages as the representative URL.
> Duplicate clusters: 2,310 URLs across 6 versions
E-214MAJArchitecture
Hash-fragment routing (/docs#/collectors/otel) collapsed thousands of pages into one URL as far as search engines could tell.
> Unique crawlable URLs: 412 of 6,040
W-318MAJCrawl budget
Faceted API-explorer parameters and session tokens created an effectively infinite URL space. Googlebot spent 71% of its requests on it.
> Crawl spent on non-canonical URLs: 71%
W-322MINCrawl budget
Soft-404s on renamed pages returned 200 with a 'not found' component. Sitemaps listed 1,400 URLs that redirected.
> Crawl errors logged per week: 3,840
W-405MAJHreflang
German and Japanese docs were injected client-side on the same URL as English, so there was no locale URL to annotate and the DE pages were never indexed.
> Indexed DE/JA docs pages: 0
Calibration · the strategy
Four channels, each tuned separately.
The work ran on our Signal plan for technical SEO. Each tactic fixed one source of noise, and each came with a check so the fix could not quietly regress on the next release.
CH1
JavaScript rendering
Moved the docs from a client-only SPA to static pre-rendering at build time, with incremental regeneration on each release. Every page now ships its full text, headings and code samples in the first HTML response; JavaScript only adds interactivity.
Raw-HTML parity check run in CI: the build fails if rendered and raw text differ by more than 2%.
CH2
Docs architecture
Replaced hash routes with real paths (/docs/collectors/otel), turned span navigation into anchor links, and introduced a 'latest' canonical with versioned pages pointing at it. Breadcrumbs and a 3-click maximum depth to any guide.
Blocked explorer facets and session parameters in robots.txt, stripped tokens from links, returned real 404/410 status codes, and split sitemaps by section with accurate lastmod dates fed from the docs repo.
Share of crawl on canonical docs URLs: 29% → 94%.
CH4
Hreflang
Gave each locale its own subfolder (/de/, /ja/) and server-rendered it, then added reciprocal hreflang and x-default in both the HTML head and the sitemaps, generated from the translation manifest so they cannot drift.
Hreflang return-tag errors: 1,960 → 0.
Time base · 1 div = 1 month
8 months, probe to lock.
M01 · PROBE
Crawl and render audit
Log-file analysis of 30 days of Googlebot hits, raw vs rendered HTML diff on every URL, and a baseline of 1,120 indexed docs pages.
M02 · PROBE
Pre-render pilot
Collector guides (420 pages) pre-rendered behind a flag. Indexed count in that section rose from 61 to 388 in five weeks.
M03 · CALIBRATE
Rendering rollout
Static rendering shipped for all 6,040 pages. CI parity check added. Span navigation rebuilt as real links.
M04 · CALIBRATE
URL migration
Hash routes replaced with paths, 6,000+ one-to-one 301s, version canonicals pointed at 'latest'.
M05 · FILTER
Crawl budget clean-up
Facet and token parameters blocked, soft-404s fixed, sitemaps split by section with true lastmod.
M06 · FILTER
Locale subfolders
German and Japanese docs moved to /de/ and /ja/, server-rendered, with reciprocal hreflang.
M07 · LOCK
Content gaps
Comparison and how-to pages written for 140 developer queries where competitors' guides ranked and Ferrovane had no page.
M08 · LOCK
Monitoring and hand-off
Render, index and hreflang checks wired into the release pipeline, with alerts to the docs team.
Readout · results
Tune each channel. The signal was there all along.
Indexed docs pages 1,120 → 4,940
Organic sign-ups +88%
Crawl errors -93%
Developer-query top-10s +620
Modelled scenario for a fictional business — not a client result. Rankings and traffic are never guaranteed; see our Terms.
0/4 channels locked
CH1 · pages in indexNoise
Indexed docs pages
1,120 → 4,940
CH2 · organic sign-ups, index = 100Noise
Organic sign-ups
+88%
CH3 · crawl errors / weekNoise
Crawl errors
-93%
CH4 · new developer-query top-10sNoise
Developer-query top-10s
+620
Turn a knob (drag, or focus it and use the arrow keys) to pull each metric out of the noise.
Developer queries, before and after
Google position, modelled. “—” means not in the top 100.
Query
Loc
Vol/mo
M0
M8
opentelemetry collector config
EN
6.6k
48
#3
structured logging kubernetes
EN
2.9k
—
#6
fluent bit json parser
EN
3.4k
22
#4
trace sampling strategies
EN
1.6k
—
#8
promql vs logql
EN
1.3k
67
#5
log retention policy gdpr
EN
880
31
#2
log management dsgvo
DE
1.1k
—
#1
opentelemetry exporter einrichten
DE
590
—
#3
ログ 監視 ツール 比較
JA
1.9k
—
#7
One results page, before and after
“opentelemetry collector config”, modelled.
M0 · #48
1. Competitor tutorial: OTel collector setup
2. Community wiki: collector config reference
3. Blog: shipping logs with the collector
… Ferrovane on page 5
M8 · #3
1. OpenTelemetry project docs
2. Competitor tutorial: OTel collector setup
3. Ferrovane Docs › Collectors › Configure the OTel collector
End of trace
Get a plan like this
If search engines can’t see what your users see, Signal starts with a render and crawl audit and turns the findings into fixes your engineers can ship.