JavaScript SEO and rendering
How Google crawls, renders and indexes JavaScript pages: the rendering queue, soft 404s in SPAs, History API routing, hydration pitfalls, and fixes from Google Search Central.
Modern sites often ship an empty shell of HTML and fill it in with JavaScript. Google Search can run that JavaScript, but rendering is a separate step from crawling, and several SPA patterns confuse indexing. This guide sticks to what Google Search Central documents about how Googlebot processes JavaScript pages and how to avoid the usual pitfalls.
How Google processes JavaScript
Google describes three main phases for JavaScript web apps: crawling, rendering, and indexing.
- Crawling. Googlebot takes a URL from the crawl queue and makes an HTTP request — but only after checking
robots.txt. If the URL (or a resource it needs) is disallowed, Googlebot skips the request. Google Search will not render JavaScript from blocked files or on blocked pages. That is one reason a careful robots.txt checklist matters for JS-heavy sites: blocking/_next/static/,/static/js/, or similar asset paths can leave Google with an empty shell. - Rendering. Pages that return a
200HTTP status (and are not blocked from indexing by a robots meta tag or header) go into a rendering queue. A headless Chromium then executes the page's JavaScript. The queue wait can be seconds or longer depending on Google's resources. Non-200responses (for example a real404) may skip rendering. - Indexing. Google uses the rendered HTML to index the page and parses it again for links to add to the crawl queue.
Server-side rendering (SSR) or pre-rendering is still recommended: Google notes it makes sites faster for users and crawlers, and not all bots run JavaScript. Faster, leaner pages also help crawl budget on large sites.
Soft 404s in single-page apps
Client-side routers often show a "not found" UI while the server still returns 200. Google calls these soft 404 errors: the page looks like an error to users but stays crawlable and may be indexed. Google documents two remedies:
- Use a JavaScript redirect to a URL where the server responds with a real
404(for example/not-found). - Add a robots meta tag with
noindexon the error view via JavaScript so the page is not indexed.
Soft 404s also waste crawling: Google's crawl-budget guidance calls out eliminating soft 404s so crawlers stop revisiting dead inventory.
Links, routing and the History API
Google can discover links only when they are HTML <a> elements with an href. Injecting links into the DOM with JavaScript is fine if they follow that pattern. For SPA routing, Google recommends the History API (/products, /services) and explicitly warns against hash fragments (#/products). The old AJAX-crawling scheme has been deprecated since 2015; fragments are not a reliable way to expose distinct URLs to Googlebot.
Titles, canonicals and robots meta
- You can set or change
<title>and meta descriptions with JavaScript; keep them unique and descriptive. - Prefer setting
rel="canonical"in HTML. If you must inject it with JavaScript, do not change it to a different URL than any canonical already in the original HTML, and avoid multiple conflicting canonical tags. - Be careful with robots meta tags. If the original HTML includes
noindex, Google may skip rendering and JavaScript execution — so using JS to remove anoindexmay not work. If you want the page indexed, do not putnoindexin the initial HTML.
Common pitfalls from Google's troubleshooting guide
- Blocked JS/CSS. If robots.txt blocks the scripts or styles needed to render content, Google cannot see the page as users do. Verify with URL Inspection or the Rich Results Test and look at the rendered HTML.
- Aggressive caching without fingerprinting. Googlebot caches aggressively; the Web Rendering Service may ignore caching headers and reuse outdated JS/CSS. Content fingerprinting in filenames (for example
main.2bb85551.js) avoids serving stale bundles. - State across page loads. WRS clears localStorage, sessionStorage and cookies between page loads. Do not rely on persisted client state to show primary content.
- Permission APIs and non-HTTP transports. Googlebot declines user permission prompts (camera, etc.) and retrieves content over HTTP — not WebSockets or WebRTC. Provide HTTP fallbacks and feature detection for critical APIs.
- Web components. Google flattens light and shadow DOM when rendering. Content that never appears in the rendered HTML will not be indexed; use slots (or equivalent) so important text is visible after flatten.
- JS paywalls that ship full HTML then hide it. Google notes that returning the full content in the server response and only hiding it with JavaScript is not a reliable way to limit access.
Hydration and content parity
Google indexes the rendered HTML after JavaScript runs. If the HTML your server sends differs sharply from what appears after client hydration — for example, primary copy that only mounts in the browser, or links that never appear as crawlable <a href> elements — Search may miss content users eventually see. Prefer patterns where the important text, headings and links exist in the rendered output that URL Inspection shows, not only after a late client-only fetch.
Structured data can be injected with JavaScript as JSON-LD; Google documents that this is supported, and recommends testing so the rendered page still contains the markup you expect. Lazy-loaded images and content should follow Google's lazy-loading guidance so off-screen content intended for indexing can still be discovered.
How to verify
Google recommends the URL Inspection Tool in Search Console and the Rich Results Test to see loaded resources, console errors, and the rendered DOM. Confirm that primary content, titles, and links appear after rendering. Pair that with a clean robots.txt, accurate sitemaps, and a quick pass through the free Crawl Budget Snapshot if you want a public robots/sitemap sanity check. More guides live on the guides index.
Check your own site
The free Crawl Budget Snapshot fetches a site's public robots.txt and sitemaps and gives a quick crawl-waste score with suggested fixes. It is a starting point, not a replacement for Search Console or log analysis.
References
- Google: Understand the JavaScript SEO basics
- Google: Fix Search-related JavaScript problems
- Google: HTTP status codes and Google Search
- web.dev: HTTP caching / long-lived caching strategies