The reason to test this way is that serving decisions happen server-side and script-side, keyed on signals that differ between a real phone, an emulated phone and a crawler. The analysis piece published alongside this guide shows a demo page serving three different bodies to those three visitors. This is the procedure for catching that on a site you care about.
Step 1. Fetch as Googlebot smartphone
Google publishes its crawler user agent strings in the crawler documentation; the smartphone one embeds “Googlebot/2.1” inside a mobile Chrome string [VERIFY: paste the current exact string from the documentation into the command below].
curl -sL -A "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
https://example.com/page/ -o as-googlebot.html
Remember what this is and is not. It shows what the server serves to something claiming to be Googlebot, before JavaScript. Sites that verify crawler IPs will not treat you as Googlebot, which is itself worth knowing; if as-googlebot.html differs from what URL Inspection shows in step 4, IP-based treatment is likely.
Step 2. Fetch as ordinary mobile Chrome
curl -sL -A "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36" \
https://example.com/page/ -o as-mobile.html
diff <(sed 's/<[^>]*>//g' as-googlebot.html | tr -s ' \n' ' \n') \
<(sed 's/<[^>]*>//g' as-mobile.html | tr -s ' \n' ' \n') | head -50
An empty diff, ignoring nonces and timestamps, means the server does not branch on the crawler token. A large diff means it does, and every line of it is something Google sees that your visitors do not, or the reverse, which is the definition of the problem.
Step 3. Look at the page on a real phone
Connect an Android device by USB, enable developer options and USB debugging, open the page in Chrome on the phone, and open chrome://inspect#devices in Chrome on the computer. Inspect the tab and read the served DOM:
copy(document.documentElement.outerHTML)
This is the ground truth for what a human on a phone receives, with real touch support, real Client Hints and a real user agent. Compare its text against as-mobile.html. Differences here are script-side, since the server saw nearly the same request, and usually trace to a device-detection library deciding after load.
Step 4. Read Google’s own copy
URL Inspection in Search Console, on a property you control, shows the crawled page’s rendered HTML, the smartphone crawler being the default for most sites. This is the only view that is not a simulation. Compare its text with the phone’s DOM from step 3. Agreement means Google gets the human page. A gap here, after steps 1 and 2 came back clean, points at rendering behaviour rather than serving, and the render-audit piece published alongside covers that path.
Step 5. See what your server is told
The signals a server can branch on are visible in its own logs or an echo endpoint. If you can add a temporary endpoint that returns the request headers, hit it from each of the four contexts and record User-Agent, Sec-CH-UA, Sec-CH-UA-Mobile, Sec-CH-UA-Platform and Viewport-Width. A real phone on Chrome sends Sec-CH-UA-Mobile: ?1. Desktop Chrome in device emulation sends emulated values that are close but not identical, and curl sends none. Whatever branching logic your stack contains, these headers are its inputs, and seeing them side by side usually explains every diff from the earlier steps.
Check it worked
Four artefacts on disk: as-googlebot.html, as-mobile.html, the real device DOM, and the URL Inspection rendered HTML. Word-count all four; they should agree within a few percent, and the pairwise diffs should contain nothing but nonces and timestamps. File the four files with the audit, because the next time someone says “but it looks fine on my machine”, the answer is which of the four machines.
The six things emulation gets wrong
DevTools device mode is excellent for layout work and wrong as an audit tool, in these specific ways. The user agent it sends is a preset that does not match any current real device. The Client Hints it sends are emulated and can disagree with a real handset. Touch is simulated, so maxTouchPoints and touch-event feature checks can differ. Device pixel ratio is applied to rendering but the network request context stays the desktop browser’s. Network and CPU are the desktop’s unless throttled, so timing-dependent scripts behave differently. And vendor-specific headers that carrier or OEM browsers send are absent. Any one of these can flip a device-detection branch.
Where I could be wrong
The procedure assumes the interesting differences are between device classes. Sites that branch on IP geography, login state or cookies will show diffs the four views cannot explain, and the fix is to hold those variables constant across the fetches.
Crawler user agent strings and the URL Inspection interface both change. The documentation links below are the source of truth, and the commands here should be rebuilt from them rather than copied blind in a year.
Sources
- Google Search Central, Google’s common crawlers and user agent strings
- Search Console Help, URL Inspection tool
- Google Search Central, mobile-first indexing best practices
- Chrome for Developers, remote debug Android devices
- Chrome for Developers, User-Agent Client Hints
- Chrome DevTools, device mode documentation