Googlebot Is Not Your Browser
Rendering is a separate, queued, stateless stage between crawling and indexing. Almost everything confusing about JavaScript and search comes from ignoring that.
Most writing about SEO cannot be checked. “Google prefers fresh content.” “Dwell time affects rankings.” Sourceless, unfalsifiable, and repeated until it sounds like fact.
This post stays inside what Google documents and what you can measure yourself. That turns out to be enough to answer the question engineers actually have: my page works in Chrome — why does Google see something else?
The answer is that Googlebot is not running Chrome on your page the way you are. It is running a queue.
Three stages, and the one in the middle
Google describes processing in three stages: crawling, rendering, indexing.1 The middle one is where the surprises live, and it comes with a fact most people get wrong:
“All pages with a
200HTTP status code are sent to the rendering queue, no matter whether JavaScript is present on the page.”1
Every page. Static HTML with no script tag at all still goes through the render queue. Rendering is not a special path for JavaScript-heavy sites; it is the normal path for everything.
flowchart LR D["URL<br/>discovered"] --> CQ["Crawl queue"] CQ --> F["Fetch<br/>HTTP 200"] F --> RQ["Render queue<br/>———————<br/>every 200 lands here"] RQ --> R["Headless Chromium<br/>runs the JavaScript"] R --> I["Index"] R -.->|"links found<br/>only after render"| CQ
How long does a page sit in that queue? Google’s answer is deliberately vague, and the vagueness is the point:
“The page may stay on this queue for a few seconds, but it can take longer than that.”1
That sentence is the source of a lot of folklore. It is where the “second wave of indexing” model came from — the idea that Google indexes your HTML first, then comes back days later to index the JavaScript. That model no longer appears anywhere in Google’s JavaScript documentation. I looked. What the docs describe now is one queue with variable latency, not two passes.
The practical consequence survives regardless of which model you prefer: anything that only exists after JavaScript runs is discovered later than anything in the initial HTML. Including links. A URL that appears only after hydration cannot enter the crawl queue until rendering completes.
What the renderer is not
Here is where the “it works in my browser” intuition breaks. Googlebot’s renderer is a real Chromium, but it is running under constraints your browser never has.
It keeps no state. Google is explicit: “WRS does not retain state across page loads: Local Storage and Session Storage data are cleared across page loads. HTTP Cookies are cleared across page loads.”2
Read that against how a typical app behaves. A page that reads a token from localStorage to decide what to fetch gets nothing. A “you’ve seen this already” flag never persists. Every page load is the first one, from a visitor who has never been to your site.
It says no to permissions. “Expect Googlebot to decline user permission requests.”2 Any content gated behind geolocation, notifications or the camera is content Googlebot will not reach — which is why Google’s advice is to “provide a way for users to access your content without being forced to allow camera access.”2
It cannot use what robots.txt blocks. “Google Search won’t render JavaScript from blocked files or on blocked pages.”1 Blocking /static/ or /_next/ in robots.txt to “save crawl budget” is a way to make your own site unrenderable — the fetch fails, the script never runs, and the page indexes as whatever the empty shell contained.
Where your content actually lives
Once rendering is a queued, constrained stage rather than a guarantee, the rendering-strategy question stops being about performance fashion and becomes concrete: how much of your content is in the initial HTML response, before any of this applies?
Google’s own recommendation list is short — “server-side rendering, static rendering, hydration” — and it is explicit about what not to reach for.3 Dynamic rendering, the pattern of serving a pre-rendered copy to bots and the real app to humans, is now described by Google as “a workaround and not a long-term solution,” rejected because “it creates additional complexities and resource requirements.”3
That is a notable reversal. Dynamic rendering was Google’s own recommended fix for years, and plenty of production setups still carry it. If yours does, it is now maintaining a second rendering path that the search engine it was built for has stopped endorsing.
What is not your problem
The most useful thing a post like this can do is tell you what to stop worrying about.
Crawl budget is almost certainly not your problem. Google puts a size gate on its own guide: it applies to “large sites (1 million+ unique pages) with content that changes moderately often (once a week)” or “medium or larger sites (10,000+ unique pages) with very rapidly changing content (daily).”4 The page opens by telling most readers to leave:
“If your site doesn’t have a large number of pages that change rapidly, or if your pages seem to be crawled the same day that they are published, you don’t need to read this guide.”4
A blog with fifty posts is not in that conversation. Neither is a marketing site, or most internal tools. The crawl-budget advice that circulates — trim your sitemap, block low-value paths, flatten your URL structure — is aimed at a class of site almost nobody reading it operates, and applying it is how people end up blocking the resources their own rendering depends on.
And none of the stages are guaranteed anyway. Google states it plainly: “Google doesn’t guarantee that it will crawl, index, or serve your page, even if your page follows the Google Search Essentials.”5 Indexing specifically: “Indexing isn’t guaranteed; not every page that Google processes will be indexed.”5
Crawled does not mean rendered. Rendered does not mean indexed. Indexed does not mean it appears for any query. Each arrow in that chain is a filter, and technical correctness only buys you eligibility for the next one.
What you can actually measure
Nearly everything above is unobservable from outside — you cannot watch the render queue, and Google publishes no timing you can hold them to. Two things are directly checkable.
The rendered HTML, per URL. Search Console’s URL Inspection has two modes that people routinely confuse. The indexed result shows “the most recently indexed version of a page, not the live version on the web”; the live test fetches it now.6 Both expose the useful part via View crawled page → More info or View tested page → More info, which lists “a list of resources, page code, and more information.”6
That resource list is the highest-value screen in the tool and the least used. It tells you which scripts and stylesheets failed to load during rendering — the exact failure mode that a robots.txt block or a flaky third-party host produces, and the one that is invisible in your browser because your browser fetched them fine.
The live test also renders a screenshot showing how “the Google-InspectionTool sees the page,”6 which settles “is my content actually there” in about five seconds.
A crude diff you can run yourself. Fetch the page without executing JavaScript and look at what comes back:
# What the crawler gets before anything renders.
curl -sL https://example.com/some-page | wc -c
# Is the actual content in there, or just a shell?
curl -sL https://example.com/some-page | grep -c "a distinctive phrase from the page"
If that grep returns 0, your content depends entirely on the render queue. That is not automatically wrong — Google does render — but it means the content and every link on the page are behind a stage with no promised latency.
What this costs
Server-rendering has a real price, and it is not the search engine’s problem. SSR means running your app per request; static generation means a build step that grows with page count. Choosing one for indexing reasons is choosing to pay that cost. On a site whose content is genuinely dynamic and personalized, and which nobody would search for, client-side rendering remains a perfectly good answer.
Fixing what you can measure will not fix what you cannot. Getting every page into the initial HTML makes your content eligible. It does not make it rank. Everything above buys you a place in the queue, and the queue is not the thing that decides.
The queue’s latency is unknowable, so do not build on assumptions about it. “A few seconds, but it can take longer” is all you get.1 Any plan that depends on rendering finishing within some window — a launch, an embargo, a time-sensitive page — should put the content in the initial HTML instead of betting on a queue with no SLA.
Takeaways
- Rendering is a separate, queued stage, and every 200 response goes through it — JavaScript or not. It is the normal path, not a special one.
- Googlebot’s renderer keeps no state and declines permissions. Cookies,
localStorageandsessionStorageare cleared between page loads, so anything gated on them is invisible. - Blocking JS or CSS in robots.txt breaks your own rendering. Blocked files are not fetched and not run; the page indexes as the shell.
- Links only in the rendered output are discovered late. Content behind the render queue can be indexed; links behind it delay everything downstream.
- Dynamic rendering is over. Google now calls it a workaround and recommends server-side or static rendering instead.
- Crawl budget starts at roughly a million pages. If your pages get crawled the day you publish them, the entire topic is someone else’s.
- Use URL Inspection’s resource list. It shows what failed to load during rendering — the failure your browser will never reproduce.
References
Footnotes
-
Google Search Central, Understand JavaScript SEO basics — the three stages, the render queue, and blocked resources. ↩ ↩2 ↩3 ↩4 ↩5
-
Google Search Central, Fix search-related JavaScript problems — WRS statelessness and declined permissions. ↩ ↩2 ↩3
-
Google Search Central, Dynamic rendering as a workaround — why it is no longer recommended. ↩ ↩2
-
Google Search Central, Large site owner’s guide to managing crawl budget — who the guidance is and is not for. ↩ ↩2
-
Google Search Central, How Google Search works — no guarantee of crawling, indexing or serving. ↩ ↩2
-
Google Search Console Help, URL Inspection tool — indexed result versus live test, resource list and rendered screenshot. ↩ ↩2 ↩3