JavaScript Redirects and SEO: When They Work and When They Hurt

javascript redirects

JavaScript redirects send a visitor to a different URL using code that runs in the browser, after the original page has already downloaded and started executing. Google can follow them. That is the good news, and it is also where most of the bad advice starts, because “Google can follow them” and “you should use them” are very different statements.

The short version: a server-side 301 is better in every measurable way — faster, unambiguous, visible to every crawler without rendering. Use a client-side redirect only when you genuinely cannot touch the server. Then replace it as soon as you can.

How a JavaScript redirect works

Three methods appear in the wild, and they are not equivalent:

// Replaces the current entry — no back-button trap
window.location.replace("https://example.com/new-page/");

// Adds a history entry — back button returns to the old URL
window.location.href = "https://example.com/new-page/";

// Same as href, older syntax
window.location.assign("https://example.com/new-page/");

Use replace() for anything that behaves like a permanent move. With href, pressing Back returns the user to the redirecting page, which immediately fires again — an infinite bounce that makes the back button useless.

Notice what is missing: an HTTP status code. There is no way to signal “permanent” or “temporary” from JavaScript. The server responded with 200 and a full HTML document, then the document changed its mind. Every downstream system has to infer intent rather than read it.

What Google actually does with them

Google renders pages. Googlebot crawls the raw HTML first, then queues the page for rendering in a headless Chromium instance, and only during that second pass does it execute your script and discover the redirect. When it does, it generally treats an unconditional client-side redirect much like a permanent one and passes signals to the destination.

The catch is that second pass. Rendering happens on Google’s schedule, sometimes minutes after the initial crawl and sometimes considerably longer. A server redirect is known instantly; a JavaScript one waits in a queue. On a large site that delay compounds across every affected URL, and during a migration it is the difference between a two-week recovery and a two-month one.

Other consumers are less forgiving. Many third-party crawlers, link checkers, and social media preview fetchers do not execute JavaScript at all. To them, your redirecting page is just a page — so a redirected URL with valuable backlinks may not pass equity through the tools your stakeholders check.

Rendering also fails more often than people assume. A script blocked by a consent banner, a resource blocked in robots.txt, a JavaScript error thrown before your redirect line runs — any of these leaves the crawler sitting on the original URL with no idea a move happened. A 301 has no such failure mode. It is a header, and headers do not depend on anything executing correctly.

The costs users pay

A server redirect is a few hundred bytes and no rendering. A JavaScript redirect requires the browser to resolve DNS, open a connection, download the HTML, parse it, fetch and execute the script, and only then start the whole sequence again for the destination. On a slow mobile connection that is easily an extra second or two.

That cost lands directly on Core Web Vitals. Largest Contentful Paint is measured against a 2.5-second threshold at the 75th percentile of real users, and a client-side redirect eats a meaningful slice of that budget before the destination page has begun loading. If the redirecting page renders any visible content before the script fires, you also get a flash of the wrong page — bad for perceived quality, and potentially a Cumulative Layout Shift problem on the way out — and CLS is judged against a 0.1 threshold at the same 75th percentile of real users.

Analytics suffers too. A client-side redirect usually fires an extra pageview on the intermediate URL, inflating your session counts and polluting landing-page reports with addresses nobody meant to visit. Referrer data is often lost in the hop as well, which quietly breaks attribution for whichever channel sent the traffic.

When a JavaScript redirect is a reasonable choice

There are legitimate cases:

  • You have no server access. A hosted platform that lets you inject a script tag but not configure routing.
  • The redirect depends on client-side state that the server cannot see — a value in localStorage, a rendering capability check, a single-page app’s internal routing.
  • Post-action navigation inside an application after a form submission or login, on URLs you never wanted indexed anyway.
  • A short-lived stopgap while a proper server rule works through your deploy process.

What is not legitimate is using one to show different destinations to users and crawlers. A redirect that fires only when the visitor is not Googlebot is cloaking, and it is a policy violation with real consequences. Redirect logic must be unconditional with respect to who is asking.

Meta refresh: the other client-side option

The older alternative sits in the HTML head and needs no script:

<meta http-equiv="refresh" content="0;url=https://example.com/new-page/">

Set to zero seconds, Google treats it broadly like a permanent redirect, and it has one advantage over JavaScript: it is in the initial HTML, so it does not wait for rendering. Any delay above zero, though, is treated as temporary at best, and a delayed refresh with a “you are being redirected” interstitial is an accessibility problem — screen reader users and anyone reading slowly get the page yanked out from under them. If you must go client-side, an instant meta refresh is usually the lesser evil.

The alternatives, ranked

  1. 301 or 308 at the server or CDN for a permanent move. Always the first choice.
  2. 302 or 307 where the move genuinely is temporary and the original URL stays canonical.
  3. Edge redirect rules if you cannot reach the origin — Cloudflare Bulk Redirects and equivalents give you real HTTP status codes without touching the application.
  4. Canonical tag when the duplicate must stay reachable by users. That is a consolidation problem, not a redirect problem.
  5. Instant meta refresh when you control only the HTML.
  6. JavaScript redirect when you control only the script.

Finding and auditing them

Client-side redirects hide from ordinary inspection, which is the practical problem. A URL that returns 200 in a crawl but lands somewhere else in a browser is invisible unless your tooling renders the page. Compare the raw HTML response against the rendered result — Search Console’s URL Inspection tool shows you both, and a mismatch in the final URL is your signal.

At site scale you want a crawl that surfaces the whole picture: which URLs redirect, where they go, how many hops it takes, and what titles and tags exist at each end. SEO Rocket’s technical audit crawls entire sites — verified past 900 pages — and reports evidence per issue rather than a summary count, so you get the exact URLs to hand a developer instead of a number to go hunting behind. Its PageSpeed and Core Web Vitals view puts real-user field data next to lab metrics, which is where the latency cost of client-side redirects becomes something you can actually argue with. It does not deploy redirects or change your server configuration — that work stays on your side.

Audit after every platform change, and treat any JavaScript redirect you find on an indexable URL as technical debt with a due date. It probably works. It is still costing you time you did not need to spend.