This post is for the person who owns technical SEO on a hospital or multi-clinic site and has a nagging suspicion that Google is seeing something different from what the CMS outputs. It assumes you can run a Node script, you have a crawl of the site, and your templates use some client-side JavaScript. You do not need to be a developer to follow the logic, but you will need one to run the pipeline.
The question it answers is narrow: for a sample of about 2,000 medical pages, which fields differ between the raw HTML the server sends and the DOM after a browser has run the scripts, and which templates depend on JavaScript for something Search needs? That includes the title, canonical, robots directives, headings, main content, internal links and structured data.
We describe a method, not a result. We do not publish findings from client crawls here. What you get is the sampling plan, the fields to extract, the diff rules, the thresholds to set for yourself and the failure modes that waste a week if you meet them unprepared.
Why the difference matters on medical sites
Google crawls a URL first and renders it later. Its own JavaScript SEO basics say that pages with a 200 status are queued for rendering, and that rendering may be skipped when a robots meta tag or header says not to index. The same page warns that if the raw HTML contains noindex, Google may skip rendering and JavaScript execution, so removing it with a script is unreliable.
Several things follow for healthcare sites.
- A doctor page built as a client-side app can show an empty shell in raw HTML. Everything Search needs then depends on the render step.
- A canonical set or changed by script can disagree with the one in the raw HTML. Google advises against using JavaScript to change the canonical to something else, and says to keep one tag whose value matches the original HTML.
- JSON-LD injected by a tag manager exists only after rendering. Google allows this, but notes errors are easy to introduce.
- A booking widget, "load more" button or tabbed accordion may hide the clinical text, or the internal links to treatment pages, until a click.
None of these is a ranking penalty. They are dependencies. Each dependency is a place where a deployment, a slow third-party script or a blocked resource can quietly remove content that the raw HTML never contained.
The technical SEO audit usually starts with a URL Inspection check on a handful of pages. That is the right first step and the wrong place to stop, because a template problem affects thousands of URLs. A scripted comparison turns "this page looks fine" into "this template is safe or it is not".
Step 1: Build a stratified sample, not a random one
Take your sitemap or crawl export and tag every URL with a template. On a hospital site the templates are usually: home, specialty hub, treatment page, condition page, doctor profile, location or branch page, blog article, tool or calculator, and enquiry or booking page. A group with programmatic pages will have a further template per locality. The programmatic location pages post covers how those pages go thin; here we only care which template each belongs to.
Then sample by template. For 2,000 URLs, a workable split is to give every template a floor of 50 URLs and share the remainder in proportion to template size, with an extra weight on templates that carry enquiry traffic. Include:
- the 100 URLs with the most Search Console clicks, since they carry the risk;
- a set of pages with the fewest impressions, because rendering problems often hide there;
- every URL pattern that was changed in the last two releases.
Record the sample as a file with columns for URL, template, release tag and priority. You will rerun it, so keep it under version control with the scripts.
Step 2: Fetch each page twice
For the raw version, use a plain HTTP client. Send an ordinary desktop user agent, follow redirects, record the final URL, the status code and the response headers, and save the body. Do not pretend to be Googlebot; some firewalls block spoofed bot agents, and you learn nothing about real Googlebot access from a fake one. Check access separately in the URL Inspection tool on a sample.
For the rendered version, use a headless browser. Playwright is a common choice. Load the URL, wait until the network has settled and the main content selector exists, then read the serialised DOM. Set explicit rules:
- Use a fixed viewport, a fixed locale and a clean browser profile for every page.
- Dismiss or pre-set the cookie banner the same way every time, because banners change what tag managers fire.
- Wait for a named content selector with a timeout, not only for network idle, because a page can be idle and still empty.
- Do not scroll or click unless you are testing lazy loading on purpose, and then record that choice.
- Run at low concurrency. Four parallel pages is plenty; a hospital server that serves patients should not feel your audit.
Store both bodies and the rendered DOM so a disputed diff can be inspected later.
Step 3: Extract the same fields from both
Write one extraction function and run it on both bodies. Using the same code for both removes a whole class of false differences. Extract:
| Field | How to read it | Why it matters |
|---|---|---|
| HTTP status and final URL | Response metadata | Redirect chains and soft errors |
title | First title element text, trimmed | Query matching and snippet |
| Meta description | meta[name=description] | Snippet source |
| Canonical | All link[rel=canonical] hrefs, resolved to absolute | Duplicate handling |
| Robots directives | meta[name=robots] plus X-Robots-Tag header | Indexing control |
h1 and heading outline | Text of all h1 to h3 | Page topic signals |
| Main content text | Text inside the main landmark, whitespace-normalised | What the page says |
| Word count | Words in the main content text | Quick shape check |
| Internal links | Count and set of a[href] targets in main and nav | Discovery and context |
| JSON-LD | Parsed objects: @type, @id, key properties | Entity data |
| Hreflang | All alternate links | Language targeting |
| Images | Count, count without alt | Image search, accessibility |
Normalise before comparing. Lowercase nothing, but collapse whitespace, strip tracking parameters from URLs and sort JSON-LD keys. For main content, hash the normalised text and also keep the text, so a reviewer can see the actual difference.




