How a Cached 307 Redirect Quietly Killed My Google Indexing
Iwan Efendi4 min

A cookie-based locale redirect got cached by Firebase CDN without Vary: Cookie, poisoning Googlebot with 307 redirects. Here's how I diagnosed and fixed it.
I noticed something alarming in Google Search Console last week — impressions for SnipGeek dropped from around 180 per day to nearly zero in the span of three days. No deployment errors, no downtime alerts, no content changes. Just silence from Google.
My first instinct was to check for accidental The Symptom: Homepage Redirecting to
I ran a quick
The result stopped me cold:
The homepage was returning a 307 temporary redirect to
SnipGeek is a bilingual site (English/Indonesian) built with Next.js and deployed on Firebase App Hosting. The i18n routing lives in
The logic itself seems reasonable: if a visitor has a
Firebase App Hosting sits behind a CDN. My
This told the CDN: "cache this response for 1 hour, and serve stale for up to 24 hours." The critical missing piece? No
The audit was systematic. I checked each layer that could affect indexing:
Result: Blog posts returned 200 with full HTML. But when I tested the homepage — that's where the 307 appeared.
The fix was minimal. I removed the cookie-based redirect branch entirely from
Now locale-less URLs always rewrite to the default English locale. No redirect, no cache poisoning risk.
The cookie-based language preference still works — but entirely client-side. The
While I had the hood open, I made a few more defensive changes:
The scariest part of this bug was how invisible it was. The site loaded fine in my browser (I had the cookie, so I saw the redirect and landed on
noindex tags or a broken robots.txt. Both turned out fine. The real problem was far more subtle — and honestly, I didn't expect a locale cookie to be the culprit.
The Symptom: Homepage Redirecting to /id/
curl against the live homepage to check response headers:
curl -sI https://snipgeek.com/ | grep -iE 'HTTP|location|cache|cdn'HTTP/2 307
location: /id/
cache-control: public, s-maxage=3600, stale-while-revalidate=86400
cdn-cache-status: hit
age: 2326
/id/ — the Indonesian locale — for every single visitor, including Googlebot. And the CDN had cached it for over 38 minutes already.
I checked other pages like /blog and /about — those returned 200 normally. But the homepage, the most important page for crawling, was broken.
The Root Cause: Cookie + CDN = Poison
proxy.ts (Next.js 16's replacement for middleware).
Here's the code that caused the issue:
// proxy.ts (BEFORE — the broken version)
const preferredLocale = request.cookies.get("NEXT_LOCALE")?.value;
if (pathnameIsMissingLocale) {
if (preferredLocale && preferredLocale !== i18n.defaultLocale) {
// This redirect gets cached by the CDN!
return NextResponse.redirect(
new URL(`/${preferredLocale}${pathname}`, request.url),
);
}
return NextResponse.rewrite(
new URL(`/${i18n.defaultLocale}${pathname}`, request.url),
);
}NEXT_LOCALE=id cookie, redirect them to the Indonesian version. Otherwise, rewrite to the default English locale.
The problem is what happens after the redirect response leaves the server.
Why the CDN Made It Catastrophic
next.config.ts applied this cache header to all page routes:
{
key: "Cache-Control",
value: "public, s-maxage=3600, stale-while-revalidate=86400",
}Vary: Cookie header.
So when I (or any Indonesian-preferring visitor) hit the homepage with a NEXT_LOCALE=id cookie, the CDN cached the 307 redirect. Every subsequent visitor — including Googlebot — got that cached redirect instead of the actual English homepage.
Googlebot doesn't send cookies. It expected to see HTML content at snipgeek.com/. Instead, it got bounced to /id/ over and over, depending on CDN cache timing. The result: Google stopped trusting the homepage as a stable entry point, and impressions collapsed.
The Dangerous Pattern
A cookie-based redirect + public CDN caching without
Vary: Cookie = cache poisoning. The first visitor's cookie determines what everyone else sees until the cache expires.How I Diagnosed It
1
Check robots.txt and sitemap.xml
Both were clean.robots.txt allowed / for all user agents, and sitemap.xml contained all 216 published URLs including every blog post.curl -sS https://snipgeek.com/robots.txt | head -20
curl -sS https://snipgeek.com/sitemap.xml | grep -c '<url>'2
Check meta robots tags on blog posts
I fetched a sample blog post and checked the HTML fornoindex directives:curl -sS https://snipgeek.com/blog/disable-windows-defender-windows-11 \
| grep -i 'robots'<meta name="robots" content="index, follow"/> — correct. No X-Robots-Tag header either.3
Test with Googlebot user-agent
I wanted to make sure Googlebot saw the same thing:curl -sI -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
https://snipgeek.com/blog/disable-windows-defender-windows-114
Trace the redirect origin
Theage: 2326 and cdn-cache-status: hit headers confirmed it wasn't a fresh response from the server. The CDN was serving a stale redirect. I traced it back to proxy.ts and found the cookie-based redirect branch.The Fix: Three Lines Removed
proxy.ts:
// proxy.ts (AFTER — the fixed version)
if (pathnameIsMissingLocale) {
return NextResponse.rewrite(
new URL(
`/${i18n.defaultLocale}${pathname.startsWith("/") ? "" : "/"}${pathname}`,
request.url,
),
);
}LanguageSwitcher component and the LocaleSuggestionBanner both use router.push() to navigate to the /id/ URL when the user switches languages. The cookie is still set for the banner's "don't show again" logic, but the proxy never reads it.
The Principle
Server-side redirects that depend on cookies should never be publicly cached. Either add
Vary: Cookie (which effectively disables CDN caching) or move the logic client-side.What Else I Hardened
- Removed duplicate
/enredirect fromnext.config.ts— the proxy already canonicalizes/en/*→/*with a 308 redirect. Having both was redundant. - Added explicit
robotsmeta to blog and notegenerateMetadata— previously relied on layout inheritance, which is fragile if the layout metadata changes. - Added HowTo JSON-LD schema for tutorial-tagged articles — this enables rich results in Google Search for step-by-step content.
Lessons Learned
/id/ — exactly as designed). No error logs, no 500s, no build failures.
The only signal was the Search Console graph dropping to zero. If I hadn't checked GSC that week, it could have gone unnoticed for much longer.
Three takeaways I'm keeping pinned:
- Never return a cookie-dependent redirect with
publiccache headers. If the response varies by cookie, either mark itprivateor useVary: Cookie. - Test your homepage with
curl, not a browser. Browsers carry cookies and cache state that hide CDN-level issues. - Check Search Console weekly. It's the only early warning system for crawl-level problems that don't show up in application monitoring.
curl -sI your-homepage.com right now. You might be surprised by what comes back.
References
Topics
Topics in this article
Explore related topics and continue reading similar content.
Share this article
Discussion
Preparing the comments area...