How Google Renders JavaScript: Googlebot Runs It, in a Second Pass
Does Google's crawler run JavaScript? Yes, in a render queue after the first fetch. What Googlebot reads first, what the renderer waits for, and how to check.
TL;DR. Googlebot runs JavaScript, but only in a second pass. The first fetch reads the raw HTML your server sends and decides the status code, the robots rules, the canonical and the links. Pages that return 200 then wait in a render queue until a headless Chromium loads them, runs the scripts and hands the rendered HTML to the index. Google says the wait is usually seconds and can be longer; Vercel and MERJ measured a 10-second median and about 18 hours at the 99th percentile on one site. Bingbot renders with limits it spells out, and the AI crawlers fetch once and never render. URL Inspection shows you which version of your page Google kept.
Does Google's crawler run JavaScript? Yes. Googlebot fetches the raw HTML first, queues the page for rendering, and a headless browser runs your scripts later. Google JavaScript SEO is the work of making sure both passes get what they need: a first response with the signals Google reads before rendering, and a page that renders cleanly when the queue gets to it. Everything below is what Google's own documentation says about each step, what Vercel and MERJ measured when they tested it, and how to check your own pages.
Does Googlebot run JavaScript?
Googlebot runs JavaScript on every page that returns a 200 status, whether the page needs it or not. Google's JavaScript SEO basics puts it in one sentence: "All pages with a 200 HTTP status code are sent to the rendering queue, no matter whether JavaScript is present on the page." The same page says the rendering happens in "a headless Chromium" that "renders the page and executes the JavaScript."
So a React, Vue or Angular site is not invisible to Google. The catch is the order. Google splits the work into three phases it names crawling, rendering and indexing, and your JavaScript only runs in the second one. What Google sees before that phase, and how long the gap between the phases lasts, is where JavaScript sites gain or lose.
What Googlebot does on the first fetch
The first fetch is an HTTP request with no browser attached. Google's documentation describes the sequence: Googlebot takes a URL from the crawl queue, "first checks if you allow crawling" by reading robots.txt, and if the URL is disallowed it "skips making an HTTP request to this URL." If the URL is allowed, Googlebot fetches it and "parses the response for other URLs in the href attribute of HTML links," which go back into the crawl queue.
Nothing in that step executes a script. For a server-rendered page, the response already holds the content, the links and the head tags, and Google can index most of it from this pass alone. For a client-rendered single-page app, the response is a shell with a root element and a script tag, and this pass finds a title, maybe a canonical, and no links. The JavaScript SEO guide covers what each rendering mode puts in that first response.
The size limit applies here too. Google's Googlebot page says "Googlebot crawls the first 2MB of a supported file type," that past the cutoff "Googlebot stops the fetch," and that "each resource referenced in the HTML (such as CSS and JavaScript) is fetched separately" under the same limit. A JavaScript bundle over 2MB uncompressed gets cut off, and the renderer runs whatever arrived.
What Google decides before rendering
Four things are settled from the raw HTML, before any script runs.
The status code. Google's docs say "Googlebot uses HTTP status codes to find out if something went wrong when crawling the page." Only 200 responses enter the render queue. In Vercel and MERJ's data, pages returning 304 were rendered from the original 200 content, and pages returning other 3xx, 4xx or 5xx codes were not rendered at all.
The robots meta tag. A noindex in the raw HTML ends the process. Google's docs say that when it "encounters the noindex tag, it may skip rendering and JavaScript execution." Vercel and MERJ saw the strict version of that: pages with noindex in the initial HTML "were not rendered, regardless of JS content." A script that removes the tag after load runs too late to matter.
The links. The href values Google parses from the raw HTML are the ones that enter the crawl queue now. Links that only exist after rendering enter later, after the page comes off the render queue.
The canonical. Google reads the canonical in the raw HTML, then reads it again after rendering. Its guidance for JavaScript sites is that "you shouldn't use JavaScript to change the canonical URL to something else than the URL you specified" in the raw HTML. A SPA that ships the homepage canonical in index.html and rewrites it per route gives Google two answers.
How the render queue works
A page that passes the first fetch goes into a queue, and Google's wording about the queue is loose: "The page may stay on this queue for a few seconds, but it can take longer than that." Rendering starts "Once Google's resources allow," so the queue is paced by Google's capacity rather than by anything on your site.
When the page comes off the queue, the headless Chromium loads it, runs the JavaScript, and Google "parses the rendered HTML for links again and queues the URLs it finds for crawling." Google "also uses the rendered HTML to index the page."
The queue is per page. A site with 50,000 URLs, many of them filter and sort variants, sits in it 50,000 times, and Google's capacity decides how fast that drains.
What the Web Rendering Service is
The Web Rendering Service, or WRS, is the part of Google's indexing system that loads your page in a browser. The JavaScript SEO basics page says "Google Search runs JavaScript with an evergreen version of Chromium," and the 2019 announcement that introduced it promised that "Googlebot will regularly update its rendering engine to ensure support for latest web platform features." Vercel and MERJ confirmed that in 2024: "Googlebot uses the latest stable version of Chrome/Chromium, supporting modern JS features." The version shows in your logs, because Googlebot's user agent carries a Chrome/W.X.Y.Z placeholder that Google's crawler list says is the Chrome version Googlebot uses.
Two properties of WRS matter more than the version number. It holds nothing between pages: Google's troubleshooting guide says WRS "does not retain state across page loads," and that "Local Storage and Session Storage data are cleared across page loads" and "HTTP Cookies are cleared across page loads." And it caches hard: "Googlebot caches aggressively in order to reduce network requests and resource usage," and "WRS may ignore caching headers," which "may lead WRS to use outdated JavaScript or CSS resources."
You can see which version of a page Google got on its first fetch without waiting for the queue. The Googlebot simulator requests any URL with Googlebot's user agent and shows the status code, the headers and the raw HTML that Google reads before rendering. If your title, canonical and body text are in that response, the queue only affects how fast Google confirms them. If the response is a shell, everything on the page is waiting on the second pass.
What Google waits for during a render
The renderer fetches the resources your page asks for and runs the scripts until the page settles. Google does not publish the rule it uses to decide the page is done, so the useful list is what it will and will not do.
It fetches your scripts, stylesheets and API calls, each as a separate request under the same 2MB limit, and each subject to robots.txt. A Disallow on your assets or API path leaves the renderer with nothing to run.
It does not act on the page. Google's lazy-loading guidance tells you to make sure your code does not "rely on user actions, such as scrolling or clicking, to load content," and to "make sure that your lazy-loading implementation loads all relevant content whenever it is visible in the viewport." Anything behind a click, a scroll event or a hover never loads.
It refuses permission prompts. Google's troubleshooting guide heads a section "Expect Googlebot to decline user permission requests" and explains that "Features that require user permission don't make sense for Googlebot, or for all users."
It starts clean. Vercel and MERJ saw each render happen "in a fresh browser session, without retaining cookies or state from previous renders," which matches Google's own statement that state is cleared. A consent wall or a "returning visitor" flag covers the page on every render.
It flattens web components. The guide says "WRS flattens the light DOM and shadow DOM," so a component that does not pass its light DOM content through the slot mechanism loses that content in the rendered HTML.
If your page needs a user action, a permission, a cookie or a blocked resource to show its content, the render finishes without that content, and that is the version Google indexes.
What makes a render fail or time out
Google does not document a render timeout. Its JavaScript pages describe what the renderer declines and what it clears, and they stop there. Any specific number you read for a Googlebot render timeout is someone's measurement on their site, or a guess.
What is documented is the set of conditions that leave a render incomplete or skipped:
- A non-200 status, which keeps the page out of the queue.
- A
noindexin the raw HTML, which "may skip rendering and JavaScript execution." - Resources blocked in robots.txt. Vercel and MERJ wrote that "Rendering in Google usually doesn't fail," and that when it does, the cause is most often resources blocked in robots.txt or an edge case.
- Resources over 2MB uncompressed, which are cut off mid-fetch.
- Stale cached resources, since WRS "may ignore caching headers." Google's fix is content fingerprinting: a hash of the file's content in its filename, so a changed file is a new URL. Vite, Next.js and most bundlers do this for production builds.
- JavaScript errors. Google's guide recommends "collecting and auditing JavaScript errors encountered by users, including Googlebot," and notes that a parse error cannot be logged that way.
- Slow or chained data calls. A page that waits for three API requests in a row can be captured before the third one resolves. Nothing in Google's docs says how long the renderer waits; the practical move is to not make it wait.
Across more than 100,000 Googlebot fetches on nextjs.org, Vercel and MERJ reported that "100% of HTML pages resulted in full-page renders, including pages with complex JS interactions." That is one fast, well-built site. It shows Google can render anything a browser can; it says nothing about whether your bundle, your API and your robots.txt will get there.
How long the render queue takes
Google's answer is "a few seconds, but it can take longer than that." The only public measurement with real numbers comes from Vercel and MERJ, who tracked Googlebot on nextjs.org between April 1 and April 30, 2024 and published the results on July 31, 2024. They injected a small script into HTML served to crawlers, matched more than 37,000 render beacons to the server log entry for the fetch that triggered them, and measured the gap.
| Percentile of pages | Time from crawl to finished render |
|---|---|
| 25th | Within 4 seconds |
| 50th | 10 seconds |
| 75th | 26 seconds |
| 90th | About 3 hours |
| 95th | About 6 hours |
| 99th | About 18 hours |
Three quarters of pages rendered in under half a minute. The last tenth waited hours. You do not get to choose which end of the table a page lands on.
The study also tested four beliefs about JavaScript indexing and rejected each on its data: that Google cannot render client-side JavaScript, that it treats JavaScript pages differently, that the render queue significantly hurts SEO, and that JavaScript-heavy sites are discovered more slowly. Each rejection came with a caveat for large sites: more complex JavaScript on much larger sites can affect crawl efficiency, and sites with many frequently changing pages can see crawl budget effects. nextjs.org is a documentation site Google crawls constantly. A catalog on a slower server has no published measurement, and its queue times are whatever your own logs and URL Inspection say they are.
Why URLs with query strings wait longer
The same data set split URLs by whether they carried a query string.
| URL type | 50th percentile | 75th percentile | 90th percentile |
|---|---|---|---|
| Without query string | 10 seconds | 22 seconds | About 2.5 hours |
| With query string | 13 seconds | 31 minutes | About 8.5 hours |
Medians were close. At the 75th percentile a parameter URL waited 31 minutes against 22 seconds, and at the 90th about 8.5 hours against about 2.5. Vercel and MERJ also noted that frequently updated sections of nextjs.org, such as the docs, had shorter median render times, and suggested that Google may slow re-rendering for pages that rarely change.
For a store, this is the cost of filter and sort parameters: each one is a URL that enters the render queue, waits longer than a clean URL, and takes rendering capacity from the product and category pages you want refreshed. The decision rules for which parameter URLs to keep crawlable are in faceted navigation SEO.
What Google does with the rendered HTML
The rendered page replaces the raw one for indexing. Google says it "uses the rendered HTML to index the page," and that it parses the rendered HTML "for links again." Text, headings, product descriptions and internal links built by your framework all count once the render completes.
Signals that differ between the two versions give Google two answers. The canonical is the one Google warns about by name. The same applies to a title your router sets per route while index.html ships a shared one, or a robots tag added after load. Google reads the canonical in both passes: its docs say "The best way to set the canonical URL is to use HTML," and that an injected one is picked up "when rendering the page." A disagreement between the two passes is one Google has to resolve, and it does not always resolve it your way.
Structured data added by JavaScript is read. Google's docs say you "can use JavaScript to generate the required JSON-LD and inject it into the page." The limit is on the other side: crawlers that never render never see it, and for product markup Google separately warns that markup generated by JavaScript can make Shopping crawls less frequent and less reliable.
Why Google still recommends server-side rendering
Google renders JavaScript and still tells you to avoid depending on it. The JavaScript SEO basics page says: "Keep in mind that server-side or pre-rendering is still a great idea because it makes your website faster for users and crawlers," adding that "not all bots can run JavaScript."
That second clause is why the rendering choice outlasts any Googlebot measurement. For crawlers that do not render, the first fetch is the only fetch, and a client-rendered page has nothing in it.
Google's dynamic rendering page describes serving crawlers a pre-rendered version of the page in the past tense: "Dynamic rendering was a workaround and not a long-term solution for problems with JavaScript-generated content in search engines." The page recommends "server-side rendering, static rendering, or hydration," and on cloaking says "Googlebot generally doesn't consider dynamic rendering as cloaking" as long as both versions carry similar content. The history behind that wording, and when a pre-render layer is still the right call, is in dynamic rendering SEO.
What a crawl log shows about rendering
Your server log, or a per-bot crawl log, is the only place the two passes show up as separate events.
Googlebot's first fetch of a URL arrives as one request for the HTML, with Googlebot's user agent. For a page that depends on JavaScript, the render shows up later as a burst of requests from the same user agent for the page's script bundles, stylesheets, fonts and the API endpoints the page calls on load. The gap between the HTML request and that burst is the render queue, measured on your own site. On a page Google already knows, the HTML fetch can repeat without a render burst behind it, which fits Vercel and MERJ's observation that Google re-renders pages that rarely change less often.
The AI crawlers look different. GPTBot, ClaudeBot and PerplexityBot each request the HTML and move on. No burst of asset requests follows, because nothing on their side runs the page. In Vercel and MERJ's December 2024 data, ChatGPT's and Claude's crawlers did request JavaScript files in 11.50% and 23.84% of their fetches, so you may see script requests from them in a log; those files are fetched, and the study found they are not executed.
Read with that pattern in mind, a crawl log answers across the whole site what URL Inspection answers one page at a time: which pages Google has fetched, which ones it has come back to render, and which crawlers only ever saw the raw response. The crawl log guide walks through Encited's version, which lists each bot's requests per page with the status and the outcome, so you can filter to Googlebot and watch the first fetch and the later renders on a URL you just published.
Googlebot vs Bingbot vs AI crawlers
Googlebot renders every 200 page in a queue, with the timing above. Google's Gemini uses the same infrastructure; Vercel and MERJ wrote that "Google's Gemini leverages Googlebot's infrastructure, enabling full JavaScript rendering." Google-Extended is a robots.txt token with no crawler of its own, and it changes nothing about rendering.
Bingbot renders, with limits Microsoft describes itself. Bing's October 31, 2018 post on JavaScript, dynamic rendering and cloaking says "bingbot is generally able to render JavaScript," that it may not support every framework a modern browser does, and that "it is difficult for bingbot to process JavaScript at scale on every page of every website." The same post recommends "dynamic rendering as a great alternative for websites relying heavily on JavaScript," and says it is not cloaking when the site makes "a good faith effort to return the same content to all visitors." Google agrees on cloaking and disagrees on dynamic rendering as a long-term fix. Copilot answers draw on Bing's index, so a page Bingbot cannot render is a page Copilot cannot cite.
AI crawlers do not render. Vercel and MERJ's December 17, 2024 study found that "none of the major AI crawlers currently render JavaScript," covering OpenAI's GPTBot, OAI-SearchBot and ChatGPT-User, Anthropic's ClaudeBot, PerplexityBot, Meta-ExternalAgent and Bytespider. OAI-SearchBot is OpenAI's search crawler and GPTBot its training crawler, and both read the same raw HTML. The same study found Applebot does render, "through a browser-based crawler, similar to Googlebot." The per-crawler detail, including the ones nobody has measured yet, is in do AI crawlers render JavaScript.
Google indexes what your page renders to, Bing indexes what it can afford to render, and AI crawlers index what your server sends. A page can be fully indexed in Google and absent from every AI answer for the same reason, and SPA SEO covers the fixes in order of effort.
How to confirm what Google rendered with URL Inspection
URL Inspection in Search Console is the one place that shows Google's own render of your page. Google's help page describes two views, and they answer different questions.
View crawled page shows what Google indexed the last time it crawled the URL: "the raw HTML returned, the HTTP headers, JavaScript console output, and any page resources loaded." Use it to see whether the version in the index has your content. If the HTML view holds the empty shell while your page has content, Google indexed the first pass.
Test live URL, then View tested page, renders the page fresh with Google's renderer and adds "a screenshot of the rendered page." Google notes that "A screenshot of the rendered page is available only in a live test." Use it to see whether the page can render at all, and read the More info panel for the page resources Google could not load and the console output from your scripts. A resource blocked by robots.txt or an uncaught exception shows up here before it shows up as a missing page in the index.
Two things the live test does not tell you. It "does not test for all possible indexing issues," and it renders one URL on demand, so it says nothing about where that URL sits in the queue. A page that renders cleanly in the live test and still shows the shell under View crawled page has a render that has not happened yet, or one that failed on a resource that worked today.
For the whole sequence of checks, from the raw fetch to the rendered diff to the log entry, the walkthrough is in how to test JavaScript SEO.
Google JavaScript SEO: get the content into the first fetch
A page whose content is already in the raw HTML skips every problem above, because there is nothing for the render queue to add. Google indexes it on the first pass, the render confirms it, and the AI crawlers get the same page as everyone else.
Server-side rendering and static generation put the content there at build or request time. A pre-render layer puts it there for crawlers only, by serving a snapshot of the rendered page to crawler user agents while people get the app. Encited's pre-rendering does this without code changes: snapshots are stored across 300+ locations, each URL group refreshes daily, weekly, monthly or quarterly, you can re-cache a page by hand after a deploy, and a page can signal it is ready with window.prerenderReady = true or send a real 404 with <meta name="prerender-status-code" content="404">. Google's render queue still runs on those pages. It has nothing left to find.
FAQ
Does Google crawler run JavaScript? Yes. Googlebot fetches the raw HTML first, then a headless Chromium renders every page that returned 200 and runs its JavaScript. Google indexes the rendered HTML. Google's documentation says the wait between the two can be a few seconds or longer.
How long does Google take to render JavaScript? Google says "a few seconds, but it can take longer than that." Vercel and MERJ measured a 10-second median, 26 seconds at the 75th percentile and about 18 hours at the 99th on nextjs.org in April 2024. URLs with query strings waited longer at the tail.
Why does Google show my JavaScript page as a blank page or a soft 404? The render finished without your content. The usual causes are a script or API blocked in robots.txt, content that waits for a click or scroll, a consent wall, a JavaScript error, or a server that returns 200 for a route that does not exist. The live test in URL Inspection shows the failed resources and console errors.
Does Google render pages marked noindex?
Google says it "may skip rendering and JavaScript execution" when it finds noindex, and in Vercel and MERJ's data pages with noindex in the raw HTML were not rendered at all. Removing the tag with JavaScript does not change that.
Does Bing render JavaScript? Yes, within limits. Bing's own post says "bingbot is generally able to render JavaScript" and that rendering every page of every site at scale is difficult, and it recommends dynamic rendering for sites that rely heavily on JavaScript.
Do ChatGPT, Claude and Perplexity render JavaScript? No. Vercel and MERJ found that none of the major AI crawlers render it. They read the raw HTML your server sends, so a client-rendered page gives them nothing to cite.
Is dynamic rendering still allowed? Yes. Google's page says it "was a workaround and not a long-term solution," recommends server-side rendering, static rendering or hydration, and says "Googlebot generally doesn't consider dynamic rendering as cloaking." Bing recommends it outright.
