Identify and remove fake performance apps
Some "speed optimization" services cheat performance testing tools by detecting when those tools are running and serving altered pages that score well but provide no real benefit. These services detect testing tools, serve manipulated pages, and claim improvement, while real users see no benefit and performance can even worsen from added obfuscated JavaScript. Performance optimization should always aim to improve the actual user experience, not just scores.
While Shopify has removed most of these apps from the App Store, the same scam techniques are now commonly sold as "speed optimization" services on freelance platforms like Fiverr and Upwork. The code they inject into themes uses the same detection and manipulation patterns described below.
Anchor to Optimizing the metric isn't optimizing the experienceOptimizing the metric isn't optimizing the experience
Core Web Vitals are proxies for the experience, not the experience itself. LCP approximates "the main content is visible," and INP approximates "the page responds when I touch it." A proxy can be satisfied without the thing it stands for improving at all, and that gap is exactly what these techniques exploit.
This is why a score improvement is never sufficient evidence on its own. A change that improves the experience almost always shows up as pixels painting sooner or interactions responding sooner, and you can watch both happen. A change that only moves a number leaves the filmstrip and the interaction timings looking the same as before. Whenever you evaluate an optimization, whether it comes from an app, an agency, or your own theme work, verify it against what the user sees and does rather than against the score alone.
Anchor to How these apps cheatHow these apps cheat
Anchor to LCP hijackingLCP hijacking
LCP is reported for the largest image or text block the browser paints in the viewport. The browser doesn't judge whether that element is meaningful, so any element that's large enough and paints early enough becomes the LCP candidate.
These scripts exploit that by injecting an element that paints almost immediately and covers the viewport, typically a transparent 1-pixel image scaled to full size, a full-viewport div with a background color, or a large block of text positioned off-screen but still counted as painted. The browser records that element as the LCP candidate and stops promoting later candidates, so the hero image no longer contributes to the reported metric.
Nothing about the hero image's loading changes. It still downloads at the same time, decodes at the same time, and appears on screen at the same time. The reported LCP drops because the metric now describes a different element, and because this element is served to everyone, real user monitoring reports the same improved number. Field data and lab data agree, which makes the manipulation look like a genuine win.
Anchor to Testing tool detectionTesting tool detection
The more elaborate variant serves two different pages: a stripped-down one to testing tools and the original one to real users. It distinguishes between them by fingerprinting the client at the top of the document, before anything else runs.
The signals are cheap to check and hard to hide. Lighthouse identifies itself with Chrome-Lighthouse in the user agent string and emulates a Moto G device, so a substring test for Chrome-Lighthouse, GTmetrix, or Moto G Power catches most lab runs. Hosted testing tools run on Linux while nearly all real shoppers are on Android, iOS, Windows, or macOS, so navigator.platform matching Linux x86_64 is a strong tell on its own. Some scripts also check for missing touch support, headless browser artifacts, or a zero-length navigator.plugins list.
When the fingerprint matches, the script takes a different code path: skipping third-party tags, removing images, or short-circuiting the theme's own JavaScript. Real users get none of that benefit. This is the most damaging variant because the numbers you're shown describe a page that no shopper ever receives.
Anchor to Resource manipulationResource manipulation
The third family of techniques doesn't make resources cheaper, it makes them arrive after the measurement window closes.
These scripts patch the browser APIs the theme depends on, then release the work later. Common approaches include overwriting IntersectionObserver or the native loading="lazy" behavior so images never enter the viewport as far as the theme is concerned, replacing document.createElement or appendChild to intercept script tags before they're inserted, and holding third-party tags in a queue that only drains on the first scroll, mouse move, or touch. Lab tools rarely interact with the page, so the queue never drains during the test and the deferred cost is invisible.
The most destructive variant uses document.write(), document.open(), and document.close() to blank the document during a detected test run, which produces near-perfect metrics for a page with no content. On a real visit, that same interception delays hydration until after the shopper has already tried to tap something, which shows up as poor INP and as controls that look ready but don't respond.
Anchor to Compare scores across toolsCompare scores across tools
- Run PageSpeed Insights on your page.
- Run Lighthouse in Chrome DevTools (Incognito mode).
- If scores differ by 20 or more points, then you're likely affected.
- Check PageSpeed Insights screenshots. If the page looks broken, then you're affected.
- Inspect the Largest Contentful Paint element diagnostic. If it looks artificial, then the metric is cheated.
- Check the Shopify Web Performance Dashboard. Real users are usually faster than lab tests, not slower.
Anchor to Confirm the mechanism in DevToolsConfirm the mechanism in Dev Tools
A score comparison tells you something is wrong. These checks tell you what, and each one distinguishes a metric that moved from an experience that improved.
Anchor to Check whether the LCP candidate changed or the pixels arrived soonerCheck whether the LCP candidate changed or the pixels arrived sooner
- Open the Performance panel in Chrome DevTools, enable Screenshots, apply mobile CPU and network throttling, and record a reload.
- Find the LCP marker on the timings track and select it. The Summary tab names the LCP element.
- Inspect that element in the Elements panel. If it's a transparent image, an empty
div, an overlay, or anything a shopper wouldn't describe as the main content of the page, then the metric is measuring an injected element rather than your content. - Scrub the filmstrip to the LCP timestamp and compare it against the frame where the hero image actually becomes visible. If the hero appears well after the reported LCP, then the number improved without the page getting faster.
Run this before and after disabling the suspect code. A genuine optimization moves the frame where the hero appears. Metric manipulation moves only the marker.
Anchor to Check for testing tool sniffingCheck for testing tool sniffing
- Open the Network conditions drawer tab, clear Use browser default under User agent, and enter a string containing
Chrome-Lighthouse. - Reload and record the request list, then restore the default user agent, reload, and record it again.
- Compare the two. Requests that appear only under the default user agent, or that disappear under the Lighthouse string, mean the page branches on who's measuring it. A page with nothing to hide serves the same resources to both.
- Search the loaded sources for the fingerprints. Open the Search drawer tab, which searches across all loaded resources, and search for
Chrome-Lighthouse,Linux x86_64,Moto G, andnavigator.platform. Also search for the escaped forms, because these strings are usually obfuscated. See Removing the code for the hex encoding ofLinux x86_64.
Anchor to Check for hydration deferred until interactionCheck for hydration deferred until interaction
- Load the page and don't touch it. Let the Network panel settle.
- Move the mouse, scroll, or tap once, then watch the request list. A burst of script requests firing on first input means the page deferred work past the measurement window rather than eliminating it.
- Immediately after the page looks ready, try to use it. Open the cart drawer, change a variant, or open the menu. Controls that are painted but unresponsive mean the metric completed before the page was usable.
- Compare lab INP against field INP in the Shopify Web Performance Dashboard. Lab tools barely interact with the page, so deferred-until-interaction cost shows up in the field and not in the lab. Field INP that's much worse than lab INP is a strong signal.
Deferring work is a legitimate technique, so this check needs judgment. The question is whether the deferred work is needed for the page to function. Deferring a chat widget until interaction is a real improvement. Deferring the theme's own variant selector until interaction moves the metric and makes the page worse.
Anchor to What Theme Check does and doesn't catchWhat Theme Check does and doesn't catch
No Theme Check rule detects metric manipulation. Theme Check analyzes theme source for known patterns and can't reason about whether a script is gaming a measurement, and injected code of this kind is deliberately obfuscated to defeat pattern matching. Detection is manual.
Two existing rules can help surface the injection point, because these services almost always load their payload from their own domain:
RemoteAssetflags assets loaded from third-party domains instead of the Shopify CDN. Run it and treat any domain you don't recognize as something to investigate.ParserBlockingScriptflags script tags withoutdeferorasync. Fingerprinting code has to run before the page renders, so it's often parser-blocking by necessity.
Neither rule is evidence of manipulation on its own. Both give you a short list of injected code to read.
Anchor to Removing the codeRemoving the code
-
Audit your theme files:
- Check
snippets/social-meta-tags.liquid. - Check
snippets/meta-tags.liquid. - Check
layout/theme.liquid. - Look for obfuscated JavaScript.
- Check
-
Search for suspicious code patterns:
"Linux x86_64"or the hex equivalent\x4c\x69\x6e\x75\x78\x20\x78\x38\x36\x5f\x36\x34.- References to
"Lighthouse","GTmetrix", or"Moto G". - Transparent fullscreen images or
divelements. document.write(),document.open(), ordocument.close().
-
Remove the offending code:
- Test in a duplicate theme first.
- Verify that the page still functions correctly.
- Confirm that the code is malicious first. Some legitimate uses exist.
Anchor to ReferencesReferences
- Don't get scammed by fake performance experts and apps
RemoteAssetTheme CheckParserBlockingScriptTheme Check- Lab vs. field data
- Build a sustainable performance practice