Skip to content

Case study: a single page application that Google received empty

By Carlos Alberto Cardona B. · published · updated

This is the case that taught me not to trust a dashboard. The site looked perfect in the browser and, to Google, it was a blank page repeated 24 times. In job-ad language: a single page application SEO problem, JavaScript rendering, soft errors and redirect mapping.

What follows is the measured problem, what I did to fix it, and how it looks in Search Console two months later. At the end is what I can't claim, which counts too.

What needed solving

regresoempresarial.com is the business insolvency consultancy site of Carlos Alberto Cardona O., my father. It's built on Base44, a platform that generates React applications: the content is drawn with JavaScript after the page loads.

In the browser everything looked fine. Requested as Googlebot, the home page returned the title and an empty container. The search engine had to execute JavaScript to see anything, and when it did, it received the same body at each of the 24 addresses.

$ curl -A Googlebot https://regresoempresarial.com/ · before (HTML reconstructed from the measurement)
<title>Inicio</title><div id="root"></div>→ 0 words of content for the crawler
$ curl -A Googlebot https://regresoempresarial.com/ · Sep 19, 2026
<title>Reorganización empresarial en Bogotá | …</title><h1>…</h1> + 1,674 visible words + JSON-LD→ Google receives the full page
  • Routes that didn't exist answered 200 with the home page: to Google they were duplicate pages, not errors.

  • Redirects were temporary (302) instead of permanent (301), so the search engine consolidated nothing.

  • Titles and descriptions were defined in three different places and didn't match each other.

What was done

I didn't hand-code anything. I specified each piece, generated it with Claude Code, tested it with two inputs that had to produce different outputs, and verified it in production with the Googlebot user agent. When a test returned the same thing for two inputs, the piece started over.

  • Content served from the first byte: at build time an HTML file is generated per route with its text, its metadata and its structured data (service page, in Spanish). Google no longer needs to execute JavaScript to read the page.

  • A Cloudflare Worker in front of the site: real 301 redirects, a real 404 for what doesn't exist, and a sitemap that lists only what's indexable.

  • Metadata with a single source: an automated check warns if any copy of the titles, descriptions or routes drifts from the others.

  • Internal linking by design, measured on Sep 14, 2026 over the 24 published routes: 25 in-text links to the pillar page and 69 links to official sources such as the Superintendencia de Sociedades, the Senate and the DIAN (the tax authority).

  • 19 automated checks before every publish: visible questions match the declared ones, no code leaks into the HTML, internal links point to real routes, metadata matches across all its sources. The build fails if one doesn't pass.

$ verification in production · Sep 19, 2026
$ curl -sI https://regresoempresarial.com/sitemap.xml | grep x-edge-routerx-edge-router: authoritative-sitemap-xml$ curl -s -o /dev/null -w "%{http_code}" https://regresoempresarial.com/no-existe-123404

How it turned out later

With the owner's authorization I publish the Search Console figures. They belong to a new site in a small niche: what they show is not a business, it's that Google now receives the content and shows it.

Weekly impressions on Google, July to September 2026

Each point is a full week, Monday to Sunday. The vertical line marks the multi-route prerender commit (Aug 18, 2026); the other changes landed in the following weeks.

  • Impressions
  • Clicks
0125250375500Jul 13Jul 27Aug 10Aug 24Sep 7prerender (Aug 18 commit)impressions per week

Source: Google Search Console (API), propiedad de dominio · read on 2026-09-19 · window 2026-07-11 to 2026-09-17

Show the chart values
Week ofImpressionsClicks
2026-07-13651
2026-07-20830
2026-07-27740
2026-08-03531
2026-08-10832
2026-08-171470
2026-08-242444
2026-08-312842
2026-09-073194
  • 10.8 → 42.9

    impressions per day, July versus September

    GSC · 21 days of July · 17 of September

  • 1,504

    impressions in 120 days, with 17 clicks

    GSC · May 22 to Sep 17, 2026

  • 28.3

    average position in the window; in July it hovered around 50

    GSC · average weighted by impressions

Search Console · grouped by month · read on Sep 19, 2026

MonthDays with dataImpressionsImpressions per dayClicks
July2122710.83
August3154717.67
September (to the 17th)1773042.97

The Worker leaves a trail too. In the last 30 days it served 63,095 requests at the edge, and the status code distribution says whether the real 404 works with real traffic, not just in my terminal.

Cloudflare GraphQL Analytics, zona regresoempresarial.com · 30 days to 2026-09-19 · 63,095 requests

CodeRequestsShareWhat it means
20046,15173.2%Page or asset served
3015,7859.2%Permanent redirect (www, trailing slashes)
2044,3586.9%Correct empty response (analytics)
4043,4465.5%Nonexistent page: a real 404, not the home page in disguise
4032,3143.7%Blocked by Cloudflare (bots)
  • 53,680

    Worker invocations in 30 days

    Cloudflare GraphQL Analytics, Worker regreso-edge-router

  • 10

    errors in those 30 days, all on Sep 17 (0.02%)

    I declare them because a round zero would be a lie

There's one report the API doesn't expose and the owner exported by hand from the interface: impressions in generative AI features (AI Overviews and AI Mode). I publish it with its source because it's the only thing I can say about AI visibility without making it up.

  • 79

    impressions in AI Overviews and AI Mode, Aug 22 to Sep 17, 2026

    Google Search Console, generative AI features report (interface, Excel export)

  • 16

    distinct pages shown in those features

    from zero in the first days of the window

  • 71 of 79

    impressions from Colombia; the rest from the United States, Chile and Panama

    the report carries no clicks: impressions only

The five pages with the most impressions in AI features · Search Console interface · exported on Sep 19, 2026

PageImpressions in AI features
/blog/embargo-cuenta-empresarial-dian29
/ley-1116-historial-crediticio8
/blog/impagos-empleados5
/blog/incapacidad-pago-inminente5
/reorganizacion-vs-liquidacion5

What I learned, mistakes included

There was a moment when the Worker replaced itself after every change. I blamed the wrong cause twice, and both times the data fit.

The real cause was another integration listening to the same event. Since then, before attributing a problem, I list everything that reacts to that event.

And I learned not to trust a dashboard: the platform's said everything was fine while the served HTML said the opposite. What a dashboard says is not evidence of what gets served.

If you want to check it yourself

You don't have to take my word for it. With curl installed, three commands show what Google receives today:

$ check it from your terminal
curl -s -A "Googlebot" https://regresoempresarial.com/ley-1116 | grep -c "<p"→ dozens of paragraphs in the served HTML, without executing JavaScriptcurl -s -o /dev/null -w "%{http_code}" https://regresoempresarial.com/lo-que-sea→ 404curl -sI https://regresoempresarial.com/sitemap.xml | grep x-edge-router→ authoritative-sitemap-xml

What I can't claim

I don't publish positions per keyword: Search Console's query view anonymizes rare searches and its rows don't add up to the total. There's no Chrome real-user data either: the site doesn't have enough traffic yet for Google to report it. And the impressions in AI features come from an interface report the API doesn't expose: they get re-read by hand on every measurement and can't be automated.

And I don't attribute the curve to a single change. Between August and September there were several (prerender, Worker, linking, new content), and separating each one's effect would require an experiment I didn't run.

Next measurement

The dates are written before the data exists. If I haven't updated this page by then, call me on it.

DateWhat gets measured
30-day impressions and clicks in Search Console; Worker HTTP codes and errors
Quarterly comparison and real-user data (CrUX), if Google reports it by then

Is your site built with React, Base44 or Lovable?

I check for free what Google receives from your site and send it to you in writing.

Get a free diagnosis

About meCayu