Translated-slug churn — root cause, blast radius, remediation (340 redirects live)

2026-07-01 · Ben · report

Scope: www.vpn.com translated (non-English) content URLs · Method: every status verified LIVE against www.vpn.com as Googlebot UA (the earlier bot bug was UA-conditional, so browser-only checks are insufficient). Targets re-validated to HTTP 200.

Separate from the vpn-translate bot/redirect-ordering fix (deployed 2026-06-24 8b1b6e02). That fix is confirmed working — browser and Googlebot now return identical statuses.

1. Root cause (git-verified)

Translated slugs were LLM-generated twice, and the second run silently produced different slugs:

DateCommitModelWhat happened
2026-05-056733675Opus 4.7Translated slugs written into each translated file's permalink frontmatter. FR streaming = vpn/meilleur-vpn-pour-streaming. Live → Google indexed (last crawled Jun 13).
2026-06-2492b0e70Sonnet 4-6"Central translated-slug map." A fresh generation replaced the map with terser slugs (dropped the "pour"/"for" preposition), reset permalink to English, and created no old→new 301. Old URL now 404s.

The June style is systematically terser (meilleur-vpn-streaming, meilleur-vpn-gratuit, meilleur-vpn-android). It was regeneration drift, not a deliberate quality change. The new slugs are not better (lateral, arguably slightly worse for natural-language match).

2. Blast radius (live-verified as Googlebot)

374 translated slugs changed in the June regen (~100%, 8 languages). Broken for Google = 340 (271 hard-404 + 69 that 301 to a now-404 stale target). 34 already resolved. Per language broken: it 60, pt 51, es 49, da 48, fr 48, de 41, nl 41, ar 1, ja 1.

Scope caveat: this is the May→June churn (derived from git). Pre-May (WordPress-era) legacy URLs are a separate set (redirect-equity project). GSC's full 404 export is the authoritative superset — reconcile against it.

3. Remediation — KEEP new + 301 + freeze (NOT revert) — DONE

Reverting would orphan the currently-live/indexed/sitemapped new slugs (a 3rd churn) and requires the generator's --regenerate (flagged "DANGER: moves live URLs"). Neither slug set is better, so we consolidated on the live set.

Redirects — LIVE 2026-07-01. Imported 340 old→new 301s into REDIRECTS_KV (v5) via scripts/import-slug-churn-redirects.mjs (direct CF-API; the cf-admin hub 405s on imports). 69 updated (stale redirects) + 271 added; index total 6491 → 6762 (pages 131–136). Verified as Googlebot: 340/340 first-hop 301, 340/340 final 200, 0 broken. Redirect record: scripts/slug-churn-final-redirects.csv. askbowtie notes #103 (analysis) + #104 (deploy).

KV token gotcha: the importer must use the vpn-workers/.env token (Ben's account, KV-edit on ns 3bba17ed…). The ambient shell CLOUDFLARE_API_TOKEN is the vpn-astro/Michael's-account token → auth error on this namespace. The script reads the file token first for this reason.

Freeze. scripts/translate-slugs.mjs is already merge-only (existing slugs preserved; --regenerate is opt-in danger), so the churn was a one-time migration artifact and will not recur through normal runs. Backstop: scripts/check-slug-map-immutable.mjs — fails if any existing (englishKey, lang) slug changes/is removed (additions pass). Wire as a pre-commit hook or CI check (node scripts/check-slug-map-immutable.mjs).

4. Still open

5. Verification method (reproducible)