Skip to main content

Engineering

The one-line bug that hid every article from Google

For three months my Journal was invisible to Google. The cause was a single template literal in a Cloud Function. Here's how I finally caught it.

By Mr. Gill ·

I do SEO work for clients.

I write about it. I audit my own site every couple of quarters.

And for something like three months, every article I published was invisible to Google.

The bug was one line. Finding it took a full audit I only ran because traffic hadn't moved past maybe 80 visits a month and I was getting suspicious.

No errors, no traffic

Search Console said the site was indexed.

The sitemap was valid. Pages loaded fast. Every obvious metric said fine.

But impressions on article URLs were flat. Not growing, not dying, just flat. Clicks basically zero.

The pattern that should have clued me in earlier: homepage impressions were slowly climbing. Article impressions were nothing.

~3 monthsBug in production
1 lineCode change
~48 hoursUntil reindexed

That's backwards for a content site. Articles should eat the homepage for impressions because they target long-tail queries. If your homepage is carrying all the growth, something downstream is blocked.

The canonical

First thing you check in any SEO audit is the canonical tag. A wrong canonical is one of the few bugs where Google happily crawls the page and refuses to index it.

So I ran curl on an article and looked at what came back.

<link rel="canonical" href="https://us-central1-pixipace.cloudfunctions.net/insights/some-article" />

That's not my domain.

That's the internal Firebase Cloud Function URL. Every article was telling Google 'the real version of this content lives at cloudfunctions.net, not at pixipace.com.'

Google believed it.

A 500 error gets fixed in an hour. A wrong meta tag costs months.

How it got there

The SSR pipeline uses a Cloud Function called slugHandler to render article pages. Vercel proxies /insights/:slug to that function. The function builds the canonical URL from request.host.

Before

const baseUrl = `https://${request.get("host")}` — returns cloudfunctions.net when Vercel proxies the request.

After

const baseUrl = "https://www.pixipace.com" — hardcoded. There's one canonical origin for public articles.

When someone hits www.pixipace.com/insights/foo, Vercel rewrites the request to the Cloud Function. The function receives a request whose Host header is its own cloudfunctions URL, not the domain the user typed.

request.get("host") returned cloudfunctions.net. That string got interpolated into the canonical on every article.

Anyway. The fix is boring. It always is.

const baseUrl = "https://www.pixipace.com";

Hardcoded. One line. There's no legitimately dynamic case here.

I shipped the change, waited for the redeploy, re-ran the curl to confirm. Submitted a few articles for reindexing in Search Console. Maybe two days later, one started showing real impressions for its target query.

What stuck with me

I used to think of meta tags as a polish pass. One-time setup, move on.

This one had been sitting in production the whole time, quietly poisoning every post. No alert. No broken test. Just three months of lost indexation.

The tangent I keep coming back to: traffic plateaus aren't a marketing problem until you've ruled out the technical ones. 'We need to publish more' is tempting because it feels like effort. Usually the real answer is 'I need to stop and curl a page and read what Google is actually seeing.'

Which, yeah. Not as fun to say on a team call.

Need this built without the three-month bug?

I build considered software for founders and small teams. Surrey, BC.

Start a project →
Key takeaways
  • Don't trust request.host behind a reverse proxy. Hardcode the canonical origin.
  • Traffic plateaus are diagnostic signals, not marketing problems.
  • Run a full SEO audit every 4-6 weeks, not quarterly.
  • If homepage impressions outpace article impressions on a content site, indexation is blocked somewhere.