sfw/fix
JS window.location medium

JavaScript Redirect Not Followed by Crawlers

A client-side window.location redirect runs only after JS executes, passes no link equity, and crawlers may miss it.

What you see

<script>window.location.replace('/new-page')</script>
— served with HTTP 200, no Location header

What’s actually happening

Visitors land fine because their browser runs the script, but Search Console still indexes the old URL and the new page gets no ranking credit. A link checker (Screaming Frog with JS rendering off, curl) reports the old URL as 200 OK and never sees the destination. Worst case the page shows old content first, then jumps — which can read as cloaking.

Common causes

  • Permanent move implemented with window.location.href / location.replace instead of a server 301.
  • Meta refresh (<meta http-equiv=refresh content='0;url=...'>) used as a stand-in for a real redirect.
  • SPA router doing the redirect client-side, so the initial HTML response is 200 with no server-side hint.
  • Crawler has a limited JS rendering budget or JS disabled, so it indexes the pre-redirect HTML.
  • The redirect is gated on a cookie or geolocation that the bot doesn't have, serving it different content than users.

How to fix it

  1. Confirm there's no server redirectcurl -I https://example.com/old-page. A 200 (not 301/302) with the redirect only living in the page body confirms it's client-side and invisible to non-rendering clients.
  2. Replace permanent moves with a 301Issue the redirect at the server/CDN: Nginx 'return 301 https://example.com/new-page;', Apache Redirect 301, or a framework route. This passes link equity and every crawler honors it.
  3. If JS is unavoidable, back it with server hintsAdd <link rel=canonical> to the destination and ensure the response carries the right status. For SPAs use server-side redirects for known moved routes rather than client navigation.
  4. Re-test rendered vs rawUse Google's URL Inspection (rendered HTML) and a raw curl side by side. They should agree on the final URL; if rendered redirects but raw doesn't, fix it server-side to avoid cloaking flags.

Stop it recurring

Use a server-side 301 for any permanent move and reserve JavaScript redirects for post-login or app-state navigation only.

Related errors