Start a project

Article · Technical SEO · Rendering · AI crawlers · 12 min read

Why Google and AI see different HTML

A browser, Googlebot and an AI crawler can look at the same URL and come away with different HTML. Your browser usually runs JavaScript, waits, and shows the finished page. Many crawlers do not. Some fetch only the first response. Some render later, or with assets missing. Some hit a firewall that returns a challenge page instead of the article. If the meaning of the page is not in that first HTML, a search engine or an assistant may never see it.

On this page 10 sections
  1. Three views of the same URL
  2. What arrives before any JavaScript runs
  3. Rendering, and who is willing to do it
  4. Blocked assets, cookies and login walls
  5. Firewall and CDN rules that swap the body
  6. How to compare raw and rendered HTML
  7. Designing pages so the first response is enough
  8. A short debug sequence
  9. Key takeaways
  10. Frequently asked questions

I design pages on leej.uk so the first response is already enough to read, and I built the Rendered vs Raw HTML Diff because “it looks fine on my laptop” is not a crawler test. What follows is how I compare those views and how I keep them from drifting apart.

Three views of the same URL

It helps to name the three views explicitly:

  1. The raw response. The bytes the server or the edge returns for the first request, before any client script runs. This is what curl sees, and what a crawler that does not render sees.
  2. The rendered document. The DOM after a browser, or a rendering service, has executed JavaScript, applied CSS, and perhaps fetched extra data. This is what you see in DevTools after the page has settled.
  3. The view a particular bot actually got. That may be raw, rendered, partial, or a completely different body produced by a firewall, a cookie wall, a geo rule or a login redirect.

Google Search can render many pages, but that is not the same as a promise that every URL is rendered on every visit, or that the rendered result matches your Chrome window. AI crawlers and fetchers vary even more. Some retrieve a page only when a person asked a question. Some crawl on their own schedule. A number of them, at the time of writing, work from the raw HTML and will not run your client bundle. I will not invent a table of which named bots render, because those behaviours change; you have to test the ones you care about.

If those three views disagree about the title, the main text, the links or the prices, you do not have one page. You have a page that depends on who is asking.

This is the same class of bug as a UI that looks correct in a preview and wrong on a device, which is why I care about SwiftUI previews for design QA in app work, and why I treat raw HTML as the preview that search and AI actually get.

What arrives before any JavaScript runs

The first response is the only view you fully control. It should already contain:

A client-side app that ships an empty <div id="root"> and then fills it in is asking every non-rendering crawler to index an empty shell. That pattern can work if you pre-render or server-render the same content, and if a later render does not replace it with something else. It fails when the first response is a placeholder and the real article exists only after a JavaScript fetch.

View the raw source, not only the Inspected DOM. “View source” or curl -s shows the first body. The Elements panel shows the rendered one. The HTML Page Auditor is a quick way to see whether the raw document already has a single H1, a description and some body text.

CSS also matters here, though it is not “content” in the same sense. If the main text is in the HTML but a script or a stylesheet hides it until a cookie banner is accepted, some clients will never reach a readable state.

Rendering, and who is willing to do it

Rendering is extra work. A client has to download scripts, run them, request data, and wait for the DOM to settle. Browsers do this for people. Search engines may do it in a second wave, after they have already seen the raw HTML. Many AI fetchers, monitors and link-preview bots do not do it at all.

Even when a crawler can render:

Google’s documentation on JavaScript SEO is the place to confirm the current behaviour for Search. I treat rendering as a bonus, never as the only way the main text exists. That habit also helps people who browse with scripts blocked, and it helps the answer-engine structure of the page, which depends on headings and paragraphs being present in the HTML.

Blocked assets, cookies and login walls

A page can look complete in a logged-in Chrome session and empty to a bot for dull reasons:

This is closer to state and architecture in SwiftUI than it first appears: if the state that produces the UI is not available to the client, the UI will not appear. For a public web page, the “state” a crawler has is the URL and the first response.

Firewall and CDN rules that swap the body

Content delivery networks and bot-management products can serve a different body to a request they classify as a bot: a JavaScript challenge, a CAPTCHA, a 403, or a slim “please wait” page. Your browser, with a normal client hint profile and a solved challenge, then loads the real article. Googlebot, Bingbot or an AI fetcher may never get past the challenge, or may be allowed by a rule you have not tested since the product changed its defaults.

That is sometimes deliberate and sometimes an accident. A challenge in front of /llms.txt or a blog post you want cited is usually an accident. A challenge in front of /admin is not.

A few cautions:

The AI Crawler Firewall Rules tool is a way to think through those allow and deny decisions. The guide to controlling AI crawlers covers the policy side. After any change, fetch the public URL with a plain HTTP client and, if you can, with a verified crawler identity, and read the body you got, not the body you expected.

Sites served from Cloudflare Workers have an extra reason to be careful: the Worker is the origin for that request. There is a separate article on search-friendly Cloudflare Workers sites. The short version is that the first response from the Worker should already be the page.

How to compare raw and rendered HTML

Do not argue from screenshots. Compare the documents.

  1. Fetch the raw HTML with curl or an equivalent, from a machine that is not logged in, following redirects, on the canonical host. Save the body.
  2. Load the same URL in a clean browser profile, wait for the network to go quiet, and export the DOM or a full HTML snapshot after JavaScript has run.
  3. Diff the two. The Rendered vs Raw HTML Diff is built for this. You are looking for text, headings, links, prices and main images that appear in only one side.
  4. Repeat a raw fetch with a few user-agent strings you care about, and from outside your office network if a firewall is in play. If the body changes, you have a bot rule or a personalisation rule to inspect.
  5. Check robots.txt for the asset paths the rendered version needs, in case a rendering crawler is being told not to load them.

Ignore differences that are purely decorative: a theme class, a debug comment, a CSRF token. Do not ignore a missing H1, an empty article, a “enable JavaScript” message, or a challenge page in the raw view.

When the two views disagree, decide which one is wrong. Almost always the fix is to put the missing content into the first response, not to ask more bots to render.

Designing pages so the first response is enough

The durable design is boring, and it is the one I use:

You do not have to abandon a modern front-end. You have to stop treating an empty shell as a page.

A short debug sequence

When a URL ranks on the wrong snippet, is ignored by an assistant, or “is not being indexed” despite looking fine to you, I run this sequence:

  1. curl -sSI the canonical URL. Confirm 200, HTTPS, the expected host, and that you were not redirected to a login or a regional copy you did not intend.
  2. Read the raw body. Is the main text there? Is there one H1? Is there a noindex, a canonical to somewhere else, or a challenge page?
  3. Diff raw against rendered. Note anything important that appears only after JavaScript.
  4. Check robots.txt for the page and its assets, and check for X-Robots-Tag on the response.
  5. Fetch again with a bot user-agent, and from a second network if a firewall is possible. Compare bodies.
  6. If a CDN dashboard shows a bot score or a challenge, inspect the rule that fired, then fetch again after a change.
  7. Only then look at Search Console or an AI crawl log. The HTML disagreement is often enough to explain the symptom.

This is the same spirit as finding performance problems with Instruments: measure the actual artefact, not the one you hoped was shipped.

Key takeaways

If you want a second pair of eyes on a page that crawlers seem to be reading differently from you, tell me about it.

Frequently asked questions

Does Google render JavaScript on every page?

No, not as a guarantee. Google Search can render many pages, often after a first look at the raw HTML, but rendering can be delayed, skipped or incomplete, especially if scripts are blocked, slow or depend on user actions. Write the page so the first response already contains the main content.

Why does an AI crawler see a nearly empty page?

Often because the crawler used the raw HTML, and the raw HTML is an empty shell that your JavaScript would have filled in, or because a firewall returned a challenge instead of the article. Some AI fetchers do not run client scripts at all. Compare a plain HTTP fetch with the rendered DOM to see which case you have.

How do I compare raw HTML with the rendered DOM?

Save the first response from curl or “view source”, export the DOM after the page has settled in a clean browser, and diff them. The Rendered vs Raw HTML Diff on this site is built for that comparison. Ignore decorative differences; treat missing headings, body text or links as bugs.

Should important content require JavaScript?

No. Interaction can require JavaScript. The title, the answer, the main text and the important links should be present without it. That helps crawlers that do not render, and it gives rendering crawlers a complete document if their scripts fail.

Can a firewall show different HTML to bots?

Yes. Bot-management and CDN rules can serve a challenge, an error or a slim placeholder to clients they classify as bots, while your browser receives the real page. That is a common reason Googlebot or an AI fetcher “does not see” content you can see. Test with a plain fetch and review the rules that fire for known crawlers, without showing them a different article from the one you show people.

Written by Lee Johnson. Published 10 November 2026.

All articles · Swift A–Z · Apps

Questions, or an app in mind?

Let's bring your ideas to life. Whether you're envisioning a game-changing Apple Watch app or a transformative iOS experience, I'm here to make it happen.

Start a project