The three checks, and what each one rules out
Every JavaScript indexing problem is one of three failures, and they need completely different fixes. The reason to run the checks in a fixed order is that each one eliminates an entire category before you spend an afternoon on the next.
Pick a probe string first — six or eight words of body copy that appear nowhere else on the site. Not your H1, which usually sits in the title tag and the Open Graph markup too, so it turns up in the raw HTML whether or not the visible page rendered. A sentence from the middle of the page is a far better probe.
| Check | What it shows | What it tells you |
|---|---|---|
1. View source, or curl | The HTML the server sent, before a single script ran. | Probe string present: your content is server-rendered and JavaScript isn't the problem. Stop here. |
| 2. DevTools → Elements | The DOM your own browser built after running the page's JavaScript. | Still missing: the page is broken for humans as well as crawlers. That's a bug report, not an SEO ticket. |
| 3. URL Inspection → Test live URL | The DOM Google's renderer produced, on Google's machines, with Google's constraints. | Present at step 2, missing at step 3: something about Google's environment stops the render. This is the interesting case. |
Check one — what the server actually sent
In Chrome, view-source:https://yoursite.com/page shows the response body before any script executed. Ctrl+U does the same thing. What it deliberately does not show is anything your JavaScript added afterwards, which is the entire point of looking.
On the command line it's one instruction, and it takes your browser out of the argument:
curl -s https://yoursite.com/page | grep -c "your probe string"— a zero means the string is not in the server's response.- Run it again with
-A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)". If the two responses differ, your stack is serving different HTML by user agent — usually an accident involving a caching layer or a bot-protection rule, and a separate conversation. - While the raw response is open, check five things: the probe string, the
<title>, the canonical tag, the meta description, and whether your navigation contains real<a href>elements. Anything missing here is missing for every crawler that doesn't render, which is most of them. - If your framework ships a
__NEXT_DATA__or similar JSON blob, don't count it. Content sitting in a script tag as data is not content in the HTML — it still needs the render to become a page.
If the probe string is there, stop
You have server-rendered content. Whatever is wrong with the page is a content, linking or indexing problem rather than a rendering one, and why Google will not index a page is the better place to start. Carrying on with steps two and three from here is how a week disappears.
Check two — the rendered DOM, and how to diff it properly
Open DevTools, go to Elements, right-click the <html> node and choose Copy → Copy outerHTML. That's the DOM after your JavaScript ran. Paste it into a file and you can compare the two versions instead of squinting at them in separate tabs.
- Save the raw response:
curl -s https://yoursite.com/page > raw.html. - Save the copied DOM from the Elements panel as
rendered.html. grep -c "your probe string" raw.html rendered.htmltells you in one line which of the two holds your content.- For the shape of the gap,
diff <(sort raw.html) <(sort rendered.html)is crude but readable — it shows roughly how much of the page exists only after hydration.
Why a pass here proves less than it looks
Your browser is a generous environment. It has your cookies, a warm cache, a live session, your city, your language and a connection that resolves an API call in 200 milliseconds. Google's renderer has none of that. It starts with no stored state — local storage, session storage and cookies are cleared between page loads — so content that depends on a saved city, an accepted cookie banner or a logged-in session exists in your browser and nowhere else.
A pass at step two proves exactly one thing: the code works. It does not predict step three, and the gap between the two is where nearly every real diagnosis lives.
Check three — the live test, and the four things it does not prove
Search Console → URL Inspection → paste the URL → Test live URL → View tested page. Three tabs matter: HTML, Screenshot and More Info.
HTML is the DOM Google's renderer produced; search it for the probe string. Screenshot is what the renderer painted, and it's worth opening even when the HTML passes, because a page can contain your text and still render it behind a modal, off-screen, or white on white. More Info lists the page resources that couldn't be loaded and the JavaScript console messages from the render — which is where the cause is usually written down in plain English.
This is a live fetch, separate from the indexed version on the same screen. It reflects a fix you deployed ten minutes ago; the indexed side can be weeks old. Both are useful, and they answer different questions.
- It doesn't prove the page will be indexed. Google says a successful live test is not a guarantee — the test doesn't evaluate quality, manual actions or content removals. Rendering is a prerequisite for indexing, not a substitute for deserving it.
- It doesn't tell you what's in the index right now. For that, read the indexed-version panel and its crawled HTML. People routinely run a live test, see green, and conclude that the version Google is currently serving is fine.
- It doesn't reproduce the queue. Google renders JavaScript pages in a second pass that waits in a queue after crawling. The live test jumps that queue entirely, so it says nothing about how long your content takes to appear after you publish it.
- It doesn't reproduce an Indian mobile network. Google renders from its own infrastructure, not from a mid-range Android in Nashik on a busy cell. A page can pass the live test and still take nine seconds to assemble for the customer you're trying to reach.
The failures that only appear somewhere other than your desk
Once you've narrowed the failure to step three, these are the causes worth checking, in this order. The first accounts for more of them than everything below it combined.
- A robots.txt rule sitting over your own JavaScript. Block
/_next/static/,/assets/js/,/dist/or a CDN subdomain and Google won't render JavaScript from blocked files — it takes the shell and leaves. This is almost always inherited from a template robots.txt somebody copied years ago, and it surfaces in the More Info tab as a resource that couldn't be loaded. - A client-side fetch that never returns. Content pulled from an internal API, a third-party review widget or a PIN-code availability service. If that endpoint is slow, geo-restricted, or rate-limits the IP ranges Google renders from, the render completes without your content and nothing anywhere else reports an error.
- Content behind an interaction. Google Search does not interact with your page — no clicks, no scrolling. A tab or accordion whose contents are already in the DOM is fine. One that fires a request on click does not exist for a crawler, and nor does anything below an infinite-scroll trigger.
- A permission prompt. Googlebot declines permission requests such as camera and geolocation. A store locator that waits for the browser's location before rendering anything renders nothing at all.
- Stale cached bundles. Google caches resources aggressively and may ignore your caching headers, so a freshly deployed script isn't guaranteed to be picked up on the timetable you assumed. Fingerprinted filenames remove the question.
What to hand a developer
A developer can fix any of this in an afternoon, and will spend a week on it if the ticket says "Google can't see our content". The artefacts you now have turn it into a fifteen-minute job.
- The URL and the probe string. One URL, one exact sentence that should be in the HTML and isn't.
- The three files.
raw.htmlfrom curl,rendered.htmlfrom the Elements panel, and the HTML tab from the live test saved beside them. Which two match is the entire diagnosis. - The More Info list. Copy the blocked resources and console errors verbatim. That's the part that names the file.
- The ask, in their language. Not "fix SEO". Either: render this component's HTML on the server, or emit the title, canonical and meta description server-side, or delete this line from robots.txt, or replace this click handler with a real anchor.
- The verification step. Re-run the live test after deploy, confirm the probe string is in the HTML tab, then request indexing on that one URL and watch the indexed version catch up over the following days.