JavaScript Redirects

What a JavaScript redirect is, how Google's render pipeline treats it differently from a server-side 301, when it's an acceptable last resort, and how to implement and detect one — plus where meta refresh and the History API fit.

First published: Jun 27, 2026 · Last updated: Jul 18, 2026 · Advanced
demand #9 in Crawling#18 in How Search Works#103 in Technical SEO#136 on the site
1 evidence signal on this page

A JavaScript redirect sends users and crawlers to a new URL with client-side code (window.location.replace() or .href). It's the least reliable redirect type because Google only sees it after rendering — which can be delayed, or fail entirely, with no fixed timeline either way. Google's official order of preference is server-side (301/302/307/308) → meta refresh → JavaScript, and its docs say plainly: only use JS redirects if you can't do the other two. Once Google successfully interprets one, the target becomes a canonicalization signal — but that's not a proven guarantee of identical PageRank or ranking outcomes to a 301, so treat it as a last resort rather than a like-for-like swap. Use them on constrained platforms with no server access, for SPA error pages that point to a real 404, and little else. If you must use one, use window.location.replace() in the <head>, drop the source URL from your sitemap, and point internal links at the final destination. Meta refresh is a separate HTML-level redirect, and history.pushState()/replaceState() aren't redirects at all.

TL;DR — A JavaScript redirectA JavaScript redirect is a client-side redirect that uses code like window.location.replace() to send a visitor (and crawler) to a different URL. Because it only fires after the page is downloaded and rendered, Google prefers server-side and meta refresh redirects above it. is a client-side redirectA redirect sends browsers and crawlers from a requested URL to a different one. An HTTP redirect specifically is a 3xx status code paired with a Location header; meta refresh and JavaScript redirects achieve a similar navigation without being a 3xx response themselves. Permanent redirects (301/308) are Google's signal the target should be canonical; temporary ones (302/303/307) aren't. (window.location.replace(), .href, .assign()) that Google only processes after renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. — phase three of crawl → render → indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed.. A server-side 301 is seen at crawl time; a JS redirect waits on the render queue, and Google gives no fixed timeline for that wait — it can be quick or it can take a long while, and renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. can fail outright. Google’s documented preference order is server-side → meta refreshA meta refresh redirect is a client-side redirect written as an HTML <meta http-equiv=\"refresh\"> tag (or an equivalent HTTP Refresh header) — not an HTTP status code. The server returns a normal 200, then the browser navigates after the page loads. An instant (0-second) one is read by Google as permanent; a delayed one as temporary. → JavaScript, and the docs say to use JS redirects only when you can’t do the other two. Once Google successfully interprets one, the target becomes a permanent canonicalizationHow search engines pick one canonical URL among duplicates and consolidate signals onto it. signal — even Google used them on their own blog when nothing else worked — but that’s not documented proof of identical PageRankPageRank is Google's original recursive link-graph algorithm: a page's score depends on the scores of the pages linking to it, and in the published model each page's score is split across its outbound links (the simplified version: links are weighted votes). Google says it's evolved since launch but still part of its core ranking systems. or ranking outcomes to a 301, so they’re a last resort, not a spam signal. The legitimate uses are constrained platforms with no server config and SPA error pages that point at a real 404. Implement with window.location.replace() in the <head>, drop the source from your sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., repoint internal linksAn internal link is a hyperlink from one page on a website to another page on the same website. Internal links help search engines discover your pages and pass ranking signals (PageRank and anchor-text context) between them., and confirm GooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer. can fetch the JS. Meta refresh is HTML-level (0s = permanent, any delay = temporary), and history.pushState()/replaceState() aren’t redirects at all.

What counts as a JavaScript redirect

Script navigation depends on rendering and execution, so it is not protocol-equivalent to an HTTP redirect. Evidence for this claim Primary standard or official documentation supporting the adjacent article claim. Scope: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Confidence: high · Verified: Google: JavaScript redirects Processing is possible, not an exact-timing or indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. guarantee. Evidence for this claim Primary standard or official documentation supporting the adjacent article claim. Scope: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Confidence: high · Verified: Google: Redirects and Search

A JavaScript redirect navigates the browser to a new URL with client-side code. The common methods, and how they differ:

  • window.location.replace("url") — navigates and removes the original URL from session history. This is the one to use: the back button skips the redirect source instead of bouncing the user straight back.
  • window.location.href = "url" — navigates but keeps the original in history, so the back button returns to the redirecting page (and can create a loop). document.location.href and window.location.assign("url") behave the same way.
  • history.pushState() / history.replaceState()not redirects. They rewrite the address bar without any navigation or HTTP signal, so crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. don’t treat them as redirects. SPAs use them for in-app URL changes; they need real <a href> links (or actual navigations) to be crawlable.

The dividing line that matters: .replace(), .href, and .assign() all trigger a real document navigation (the difference between them is only what happens to session history), while pushState()/replaceState() never navigate at all — they touch history state and the address bar and nothing else. None of the four is an HTTP 301; “redirect” here is shorthand for client-side navigation, not a status code.

Meta refresh (<meta http-equiv="refresh" content="0;url=...">) is often lumped in with JS redirects, but it’s an HTML directive parsed before JavaScript runs — a separate, more reliable category, covered below.

How Google processes a JavaScript redirect

This is the cruxChrome User Experience Report — Google's public dataset of real-world (field) performance data from eligible Chrome users. It's the official field-data source behind the Core Web Vitals program.. Google’s pipeline runs in stages, and a JS redirect and a server-side redirect get caught at different ones:

  1. CrawlGooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer. fetches the URL and reads the raw HTML. A server-side 301/302/307/308 is seen right here.
  2. Render queue — pages that return 200 wait to be rendered. Google’s docs note the page “may stay on this queue for a few seconds, but it can take longer than that.” Google doesn’t publish a fixed service-level timeline beyond that, so treat the wait as unpredictable — it can be quick, or it can drag — rather than assuming any specific number of days or weeks.
  3. Render + index — headless Chromium runs the JavaScript. This is the first moment a JS redirect exists as far as Google is concerned.

The same idea I make on CrawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor. and in JavaScript SEO applies here: rendering is a separate step from fetching, and anything that depends on it inherits that delay and that risk.

And the risk is real. Google: “While Google attempts to render every URL Googlebot crawled, rendering may fail for various reasons. This means that if you set a JavaScript redirect, Google might never see it if rendering of the content failed.” (Google Search Central) During the window before the redirect is processed — and forever, if rendering fails — Google may keep the empty source page in its index.

Google’s official preference order

The redirects documentation lays out a hierarchy from most to least reliable:

  1. Server-side redirects — 301/308 (permanent), 302/307 (temporary). Best for everything: seen at crawl time, unambiguous.
  2. Meta refresh — HTML-level. A 0-second meta refresh is treated as a permanent redirect (like a 301); any delayed meta refresh is treated as temporary.
  3. JavaScript redirects — last resort.

Google’s words: “Only use JavaScript redirects if you can’t do server-side or meta refresh redirectsA meta refresh redirect is a client-side redirect written as an HTML <meta http-equiv=\"refresh\"> tag (or an equivalent HTTP Refresh header) — not an HTTP status code. The server returns a normal 200, then the browser navigates after the page loads. An instant (0-second) one is read by Google as permanent; a delayed one as temporary..” Permanent redirects pass the canonical signal to the target; temporary ones keep the original in results. (How that interacts with canonical selection is over on CanonicalizationHow search engines pick one canonical URL among duplicates and consolidate signals onto it..)

Google’s own redirects documentation lists JavaScript location navigation among its permanent redirection methods, and says the target becomes the canonicalizationHow search engines pick one canonical URL among duplicates and consolidate signals onto it. signal once Google has interpreted it — so the flat “JS redirects don’t pass PageRankPageRank is Google's original recursive link-graph algorithm: a page's score depends on the scores of the pages linking to it, and in the published model each page's score is split across its outbound links (the simplified version: links are weighted votes). Google says it's evolved since launch but still part of its core ranking systems.” claim is false. What the documentation doesn’t establish is that the outcome is identical, immediate, or as reliable as a server-side 301 — it describes the canonical signal, not a guarantee of matching PageRank flow, ranking, or timing. The honest framing: a 301 passes the signal at crawl time with near-certainty; a JS redirect passes it only if and when rendering succeeds, and Google doesn’t promise the result will match a 301’s outcome one-for-one. That gap — not lost equity — is the real cost of choosing JavaScript.

This is also why JS redirects aren’t a penalty trigger on their own. They only become a spam problem when they’re used for cloaking — showing crawlers one page and redirecting users to something different, or sending mobile users to an unrelated domain. Google’s sneaky redirects policy is about that intent, not the technique.

When a JavaScript redirect is the right tool

There are legitimate cases:

  • Constrained platforms. Some shared hosting, CDN, or CMSA content management system (CMS) is software that lets users create, manage, and publish digital content — like blog posts and pages — without writing raw code. WordPress, Drupal, and Joomla are the most common open-source CMS platforms. setups give you no access to server-side redirect rules. A JS redirect is a valid fallback — and notably, Google used JS redirects on their own Webmaster blog because, as Gary Illyes put it, “that was the only thing we could use for 1:1 redirects, and it works on Google” (OnCrawl).
  • SPA error handling. Google explicitly endorses this: “Use a JavaScript redirect to a URL for which the server responds with a 404 HTTP status codeAn HTTP status code is the three-digit number a server returns with every response to tell a browser or crawler what happened to its request — success, redirect, client error, or server error. For SEO the code matters as much as the content: it tells Google and Bing whether to index a page, follow a redirect, retry later, or drop the URL from the index..” (Google Search Central) A single-page app that resolves a bad route can redirect to a real 404 endpoint so Google processes the error correctly instead of indexing a soft 404A soft 404 is a URL that returns a success status code (usually 200 OK) even though the page is empty, missing, or shows a 'not found' message. It isn't a status code a server sends — it's a label search engines apply after comparing the response code against the rendered content, and they treat the page like a 404 for indexing..

For permanent URL migrations, this is not the tool — use a 301. I make the same point in site migrationsA site migration is any significant change to a website's URL structure, domain, platform, protocol, or hosting that can affect how search engines crawl, index, and rank it. The risk scales with how much you change at once.: JavaScript redirects are a last resort, and Google may never see them.

Static site generators: the Hugo aliases trap

A common surprise: Hugo’s aliases: frontmatter has historically generated meta refresh HTML pages, not server-side 301s — and other static generators have done similar things by default. Generator defaults change between versions, so check your current deployed version’s actual output rather than assuming; if aliases: isn’t giving you 301s, you need platform-level redirect rules (Netlify _redirects, Cloudflare WorkersA serverless function that runs at Cloudflare's global edge, close to every user., Vercel vercel.json) plus (on Hugo) disableAliases: true. I cover this in detail in Hugo SEOHugo SEO is the practice of optimizing sites built with Hugo, the Go-based static site generator. Hugo outputs finished HTML at build time, so content is in the raw HTML on the first fetch — but that alone doesn't guarantee good Core Web Vitals or rankings, and canonicals, taxonomy duplication, and aliases-vs-301s still need hands-on configuration..

Implementation best practices

If a JavaScript redirect is genuinely your only option:

  • Use window.location.replace(), not .href. As Search Engine Journal puts it, JS redirects “typically use window.location.replace() function rather than window.location.href to avoid UX redirect loopsA redirect loop is a chain of redirects that circles back on itself instead of ever reaching a live page — URL A redirects to B and B redirects back to A (or a longer cycle). No page ever returns a 200, so browsers show ERR_TOO_MANY_REDIRECTS and crawlers can't index anything. (SEJ).
  • Put it in the <head>, not the <body>. Browsers parse HTML sequentially and run scripts as they hit them, so “position JavaScript redirects in the <head> tag rather than <body> to minimize delay” (OnCrawl).
  • Redirect to the final destination in one hop. A JS redirect to a page that itself 301s elsewhere makes a chain; chains waste crawl budgetThe number of URLs an engine will crawl in a timeframe. and can surface in GSC as a redirect errorA Google Search Console Page Indexing status meaning Googlebot couldn't follow a redirect — a chain too long, a loop, a URL over the max length, or a bad/empty URL in the chain. It's a broken redirect to fix, not the normal \"Page with redirect.\".
  • Remove the source URL from your XML sitemapAn XML sitemap is a UTF-8 file listing the canonical URLs on your site (with optional lastmod) so search engines can discover and prioritize them. It's a discovery and diagnostic aid, not a guarantee of indexing — and Google ignores its priority and changefreq tags.. SitemapsA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. should list canonical, indexable URLs — not redirecting ones.
  • Repoint internal linksAn internal link is a hyperlink from one page on a website to another page on the same website. Internal links help search engines discover your pages and pass ranking signals (PageRank and anchor-text context) between them. at the destination, so they don’t route through the redirect at all.
  • Make sure Googlebot can fetch the JS. If the redirect lives in an external script blocked by robots.txt, Google can’t render it and won’t see the redirect.

How to detect JavaScript redirects

They don’t announce themselves like a 301 in a header, so you have to render:

  • A crawler with JS rendering on. OnCrawl recommends crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor. with “JavaScript rendering enabled (5-second timeout minimum)”; Screaming Frog and Ahrefs Site Audit can both render. Without rendering, a JS-redirecting page just looks like a normal 200.
  • Chrome DevTools. The Network tab (with “Preserve log”) shows the client-side navigation; the Redirect Path extension flags it too.
  • In Search ConsoleGoogle's free tool for monitoring crawling, indexing, and search performance., a successfully processed JS redirect shows up under Page with redirectA Google Search Console Page Indexing status for a URL that redirects elsewhere. It's not indexed by design because it's a redirect — the destination is a separate question, and Google says the target may or may not end up indexed — usually expected, not an error (unlike the separate \"Redirect error\"). — the same status as any redirected URL, which is normal for non-canonical sources. That label isn’t guaranteed on any given check, though: it reflects whatever Google fetched, rendered, interpreted, and canonicalized at the sampled moment, so a URL can show a different status (or no redirect status yet) between checks without that being an error on your end.

What I’d actually do

Server-side first, every time. Meta refresh (0-second) when you can edit HTML but not server config. JavaScript only when both are off the table — and then with window.location.replace() in the <head>, a clean sitemap, and a check that the redirect actually renders for Googlebot. For anything permanent or high-value, the extra reliability of a 301 is worth almost any effort to obtain.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.