Findings · 2026-09-09 · By VSNARY | Emmanuel Orta · 0 views
Our scanner counted 14 placeholders and none of the 13 photographs
A lazyload plugin puts a grey placeholder in src and the real URL one attribute over. Our image reader took the placeholder at face value and reported a page of webp photographs as having no modern images at all.
On 9 September we pointed this scanner at four of the WordPress sites it measures every day and read the image block it stores. On supremefencingdenver.com it recorded 14 data-URI images and zero in a modern format. The page serves 13 photographs, every one of them a .webp file. The scanner was not looking at the photographs at all. It was looking at the grey placeholders sitting where the photographs would load.
What the page actually serves
Fetch that homepage and count the markup by hand. There are 28 <img> tags. Fourteen of them carry src="data:image/svg+xml...", an inline placeholder a few hundred bytes long. Thirteen of those fourteen also carry a data-lazy-src attribute holding the real URL, and all thirteen of those URLs end in .webp. The fourteenth is a genuine inline SVG icon with an empty alt attribute, which is correct markup for decoration.
This is what WP Rocket does, and Perfmatters, and most lazyload implementations. The browser is handed a placeholder so nothing blocks the first paint, and JavaScript swaps the real URL in when the image approaches the viewport. The page is not hiding anything. The real address is right there in the markup, one attribute over.
What the scanner recorded
Our image reader resolved the source with a single expression: take src, and if there is no src, take data-src. A placeholder is a perfectly good string, so src was never empty, so the second half never ran. Every measurement downstream was then taken from the placeholder. A base64 SVG is not a .webp file and is not a .jpg file, so the modern-format count came back zero on a page built entirely out of modern formats, and the data-URI count came back 14 on a page with one real inline image.
Here is the same reader before and after the fix, run against the live homepages on the day of writing. The numbers on the right are what the service records now.
| site | data URIs before | modern format before | data URIs after | modern format after |
|---|---|---|---|---|
| supremefencingdenver.com | 14 | 0 | 1 | 13 |
| supremefencinglakewood.com | 14 | 0 | 1 | 7 |
| supremearvadafencing.com | 6 | 0 | 1 | 4 |
| treeservicedenverllc.com | 0 | 8 | 0 | 8 |
| arvadatreeservicellc.com | 1 | 7 | 1 | 7 |
| terrariumstation.com | 3 | 3 | 3 | 3 |
The last three rows are the controls and they matter more than the first three. treeservicedenverllc.com runs a different cache plugin that does not use placeholders, so it read correctly before and reads identically now. arvadatreeservicellc.com and terrariumstation.com are unchanged for the same reason. A fix that moved every site would have been a second bug.
Lakewood is the honest row. It has 13 real image URLs behind placeholders, but six of them are .jpg, so resolving the placeholder raises the modern-format count to 7 and not to 13. Reading the right attribute does not invent an optimisation the site has not done.
Why the count is 15 and not 28
The stored total for that page is 15, and the raw markup holds 28 image tags. That difference is deliberate and it is not part of this defect. Lazyload plugins emit a <noscript> copy of every image they defer, so a reader without JavaScript still sees the picture. Counting both copies would double every lazy-loaded site. The reader strips script and noscript blocks before it counts anything, which leaves 15: fourteen deferred images plus one icon. That was already right.
What changed and what deliberately did not
The fix is small. If src is missing or is a data URI, the reader now looks at data-lazy-src, then data-src, then data-original, then data-lazy, then the first candidate in a srcset, and uses the first one that is not itself a data URI. An image that resolves to nothing but a data URI still counts as a data URI, which is why the fencing sites still read 1 rather than 0. That last one is a real inline SVG and deserves to be counted.
No grade moved. The modern-format and data-URI figures are reported, not scored: the scored image rows are a missing alt attribute, width and height on at least nine images in ten, no lazy loading above the fold, and an image declared in the page schema and in the Open Graph tags. Those five read the tag itself rather than the resolved URL, so none of them were affected. supremefencingdenver.com graded A/89 before the fix and A/89 after it.
We are saying that plainly because it is the uncomfortable part. A wrong number that does not move a score is still a wrong number, and it is harder to catch precisely because nothing looks broken. Nobody complains about a grade they did not lose.
The pattern this belongs to
This is the third defect of the same shape in ten days. In one, four extractors required quoted attribute values, so a site serving valid minified HTML with bare attributes scored zero for mobile with a viewport tag plainly present, which we wrote up in the viewport was there all along. In another, the attribute value pattern excluded whitespace, so alt="a cedar fence" read back as an empty string and images carrying real descriptions were counted as decorative. Now this one.
All three are the same mistake. Each read an attribute at face value on a page where something had rewritten the markup between the author and the browser. A minifier removed the quotes. A performance plugin moved the URL. The parser was not wrong about the character it found; it was wrong about which character mattered.
Our own monitoring could not see any of them. Every internal check compares one of our measurements against another of our measurements, and both halves of a comparison inherit the same blind spot upstream. When the reader believes a page has no modern images, every check that depends on that reader agrees with it perfectly. We hit exactly this in the timestamp work described in our proofs said pending for 26 days, where the monitoring confirmed the job ran and never opened the file it produced.
One more piece of self-disclosure. The open-source extraction of this reader has carried the correct behaviour since 4 September, because writing the package meant writing tests against real markup rather than against the code. The service kept the old behaviour for five more days. Publishing the module is what surfaced the defect; it did not fix the thing customers were actually using until today.
What to check on your own site
If you run any lazyload plugin, open your homepage source and look at what is inside src on your hero image. If it starts with data:, then every tool reading that attribute is measuring a placeholder, not your photograph. That includes tools that grade your image optimisation, your alt coverage and your file formats, and it includes any language model summarising your page from the raw HTML.
The check is quick and it does not need us: count how many of your image tags have a data URI in src, then count how many of those carry a data attribute with a real URL. If the two numbers match, everything reading your markup naively is wrong about your images in the same direction ours was.
The public directory shows every site we measure with its current grade and the date it was last scanned. The three fencing sites in the table above were rescanned after the fix, so what is published there is the corrected reading.
Every figure above came out of this scanner.
Point it at your own domain and see the same measurements, free.
Related findings
Writing about this? Facts, live figures and marks — every number on that page is dated and traceable to a scan.