help1
shopify page speedtheme auditdebuggingcloakingshopify

Eight Lines of Theme.liquid Faked This Shopify Page Speed Score

Her store had three contradictory shopify page speed scores. The cloaking script was hidden in eight lines of theme.liquid.

help1 Team
Eight Lines of Theme.liquid Faked This Shopify Page Speed Score

A merchant emailed me last month with three numbers she could not reconcile. Her Shopify admin dashboard said her store had "Poor" mobile performance. PageSpeed Insights gave her an 87 on mobile. GTmetrix marked the homepage with an A. Three tools, three answers. The answer turned out to be eight lines wedged near the bottom of theme.liquid, between two harmless-looking comment blocks, written by someone she never met.

This post is the walkthrough of how I found those eight lines and how she got rid of them. It is not the post about why the tools disagree in general - the methodology primer covers that - and it is not the post about why merchants get sold a fake number in the first place (the culture post is here). This post is one case study and one diagnostic.

What I checked first

She had done the obvious work before she emailed. Deleted two apps she stopped using last year. Swapped her hero image to a WebP under 100KB. Hard-coded eager loading on it. PageSpeed Insights still gave her an 87 on mobile, which was the only good number she had, and the Shopify admin dashboard kept flagging her LCP as "Poor" in the field data.

I ran Lighthouse from Chrome DevTools on my own machine three times back to back and got 78, 91, 84. The variance alone told me something was off about how PSI was scoring the page. For a shopify page speed problem with this profile, high lab score and poor field data, my usual first move is the app embed toggle test. Toggle every app embed off in the theme editor, run PSI, then toggle them on one at a time and re-run. App scripts loading in the document head are the single biggest cause of a page that scores well on a fast machine but tanks on a real phone.

We did the test. The score barely moved. Whatever was happening on this store was not coming from her installed apps.

The shape of a real regression versus the shape of a lie

A real performance regression has a fingerprint. It shows up consistently across tools that measure the same thing. An app loading 400KB of JavaScript in the head will hurt your LCP whether you measure it with Lighthouse, GTmetrix, or your own DevTools. When tools disagree by 50 points, the explanation is rarely "your store is broken in three different ways." It is more often that one of the tools is being lied to.

The honest answer to "which shopify page speed score is the real one" is the field number, because it is what your customers actually experienced. The lab number is a diagnostic. That part is well established. What was interesting about this store was the size of the gap. A 5-point gap between lab and field is normal CDN variance. A 50-point gap is something else.

I almost wrote her back with the standard advice. Trust the field data. Ignore the lab scores. Move on. I had typed three paragraphs of it. Then I closed the draft, because something about her case did not fit the pattern. Her PSI score was not low. It was suspiciously high. 87 on mobile is in the top 1.83% of all Shopify stores per the 10K-store dataset that floats around r/shopify every quarter. Most stores hover around 50. A genuinely 87-scoring store should not show "Poor" in the Shopify admin dashboard. The two numbers should agree, even loosely.

That is when I started looking for a different kind of answer.

The clue I almost missed

A September 2025 r/shopify thread, "I worked inside the Shopify speed optimization scam on Fiverr," describes the cloaking technique in operational detail. A script in your theme checks the user agent string for "Chrome-Lighthouse" or "GTmetrix." When it sees one of those strings, it calls document.open() and serves an empty or stripped-down page. The bot scores a 90+. Real users on real phones see the actual store and the actual performance.

I almost did not bring this up with her. She told me she had never paid anyone for speed optimization. I believed her. Then she paused and said, "But the previous owner did, before I bought the store. The Exchange listing said the store had been 'optimized to 90+ PSI.' That was one of the reasons I bought it."

I asked her to do one thing. Open theme.liquid in the code editor and search for document.open(. Took her about thirty seconds. She found it.

What the script looked like

Eight lines, paraphrased so I am not pasting working malware into the post:

<script>
  (function() {
    var ua = navigator.userAgent || "";
    if (ua.indexOf("Chrome-Lighthouse") > -1 || ua.indexOf("GTmetrix") > -1) {
      document.open();
      document.write("<!doctype html><html><head></head><body></body></html>");
      document.close();
    }
  })();
</script>

Sitting between two innocuous-looking Shopify Polaris comment blocks that the previous owner's "consultant" had added as camouflage. The script ran before any of the page chrome rendered. When PageSpeed Insights and GTmetrix tested the page, their user agents matched, and the script rewrote the document to an empty shell. The lab tools measured a near-empty page and scored it brilliantly. Every real user with a normal Chrome user agent fell through the conditional and got the actual store, which was slow.

That was why her PSI was 87. That was why her Shopify admin dashboard, which uses CrUX field data and cannot be fooled by user-agent sniffing, kept reporting "Poor." Real Chrome users on real phones were having a real slow experience. The lab score had been lying for two years.

How to find it on your own store

The diagnostic is shorter than you would expect. Three steps:

  1. Search theme.liquid for document.open(. If it is there, it should not be. Modern Shopify themes do not call this, whether you are on Dawn, Horizon, or a premium theme. If you find it, screenshot it and the surrounding ten lines before you do anything else.

  2. Search the same file for the substrings Chrome-Lighthouse and GTmetrix. These should not appear in any theme code you wrote or bought legitimately. If they appear inside a user-agent check, that is the smoking gun.

  3. Run PSI, then run Lighthouse from Chrome DevTools on the same page. DevTools uses your real user agent, so it bypasses the cloaking conditional. If the two scores differ by more than 20 points, you have a fingerprint. The 20-point heuristic was published by Shopify's performance team in March 2024 as a manipulation indicator.

If any of those steps surfaces something you did not put there, the first 15 minutes with a help1 expert is free and we have the rest of the checklist saved as an intake. We see this pattern often enough that it has its own intake question now.

How to remove it safely

Take a duplicate of your current theme before you touch the live one. Online Store > Themes > Actions > Duplicate. Edit the duplicate. Delete the entire <script>...</script> block you found, including the surrounding comments if they look like camouflage. Save. Preview the duplicate. Run PSI against the preview URL.

Her PSI score crashed from 87 to 41 the moment I deleted the script and re-ran the test. That 41 was the truth. The cloaking script was the answer to "why don't my tools agree." It was not the answer to "why is my store slow," because once we removed it, the store was still slow.

That gap, between "what the screenshots claimed" and "what the store actually was," is the work that gets papered over by these scripts. Pulling the script out does not optimize anything. It just shows you what you actually have.

What we did next

The store had a hero video that loaded eagerly, a chat widget that ran in the document head, and a review widget that injected after first paint. None of that had been touched by the original "optimization" she paid for, because the original optimization had not done any work on her actual shopify page speed. It had only worked on the test result.

We replaced the hero video with a poster image and a click-to-play. The hero image LCP trap write-up covers that fix in more detail. We deferred the review widget below the fold. The chat widget was the harder call. The chat-widget speed penalty post covers the cost-benefit there; we ended up deferring it behind a small first-interaction trigger.

PSI mobile climbed from 41 to 63 over a week. Her admin LCP dropped from 4.8s to 2.7s in the next CrUX update, which took about three weeks to show. That was the change that mattered, because that was the change real users felt.

What I would flag earlier next time

I should have asked about previous ownership in the first email. I asked about her installed apps, her theme, her hero image, her hosting region, her admin permissions. I did not ask whether someone else had owned the store before her. That single question would have saved us about three hours. It is now the first question on my intake checklist for any merchant whose lab score and field score disagree by more than 30 points.

If your shopify page speed numbers have disagreed for months and you bought your store from someone else, open theme.liquid and search for document.open(. The whole rest of the diagnostic flows from whether or not it is there.

See also

Still stuck? Talk to an expert.

Our vetted Shopify experts can fix this issue for you in a live session. $39 per session. Your first 15 minutes are free.