Our website home page (along with some secondary subcategory navigation pages) has zero layout shift when looking at Googles Core Web Vitals statistics for the desktop. Yet the exact same responsive pages on mobile had horrible “field data” CLS stats, typically > 0.7 or worse. Synthetic “lab” testing would report the pages as zero CLS. It took several days of picking away at this mystery to figure out the cause… a Cloudflare setting.
The CF setting “Mirage” was causing the severe layout shift on mobile. Quoting CF: What does Mirage do? Mirage tailors image loading based on network connection and device type. Devices with small screens receive smaller images, and slower connections receive lower resolution images. This speeds up page rendering so users can begin interacting with your website without waiting for images to download first.
The problem in our case was that Mirage feature somehow allows the mobile device to begin rendering the page before it has the image information. Since our pages in question consist mostly of a matrix of image tiles but each also with a text link that are used for navigation, the text links all displayed first and then once the images became available, the page had to rewrite… triggering massive layout shift.
This can most easily be seen in a film strip… first without Mirage. Notice the page pops in fully rendered (and with zero layout shift).
Now with CF Mirage on the page: The text links all render first, then the images begin popping in, each triggering more and more layout shift.
Enabling the Mirage feature does exactly what CF says it does… notice the text links are visible almost half a second faster than the fully rendered page… but at a cost of massive layout shift on the page in our specific case.
What drove me crazy trying to figure this out was that all the synthetic testing I was doing, was returning results of zero CLS… yet the real world field data was showing awful CLS. I suspect the reason is that the synthetic mobile tests are not being identified by CF as being a mobile device and so CF Mirage is delivering the page using desktop behavior.
Even if you convert image to webp using Cloudflare, CLS increase.
Yeah, the issue occurs no matter what I tried in the page html (styling the img with aspect-ratio, or height+width, etc) and occurs no matter what image format (jpg, jpg+progressive, webp). That’s because it’s that the text links are displaying on the page before the image information. How CF is managing to do that is a mystery to me… at a minimum the browser knows the aspect-ratio and dimensions from the html at the time the page is loaded… so the browser should be able to leave room for the image coming later at the time it’s starts the render page, but with CF Mirage option enabled the browser page behaves differently.
Mirage shouldn’t ordinarily trigger a CLS since the images are typically pre-loaded with the dimensions already in place, if remember correctly.
If not, capturing a HAR file would be useful - let me know if you get one and I’ll DM you to grab it.
I tried testing with JS off… didn’t change anything. Looking at a waterfall, it was happening before external JS was even loaded. The page was originally designed as “responsive” and dimensions were omitted in favor of “aspect-ratio: 1/1;” css… that didn’t fix it, so I added both height/width and aspect-ratio. No Joy. I gave up and kinda not interested in debugging this further… I’m sorry I don’t have a .har file for you. The page is highly optimized and loads lighting fast on the desk top… speed index typically under 1000 ms. Turning Mirage is somehow counter productive for layoutshift… in some way I don’t understand the browser could start rendering the page before it pulled in the images… but you are right is should have known the image size from the height/width.
Ignore my previous response - we were able to replicate here and no errors are observed - we’d recommend disabling Mirage in this case as it’s not optimal for your particular website.
Simon, If you figure out how to fix it… let me know… would like the extra boost on mobile but not at the price of the massive CLS. My guess was that Mirage was somehow stripping off or changing some of the html information on the img tag… but no way to see what the source actually was on a real iPhone (it was really really bad on an iPhone X).
Will the issue be resolved or it the only solution not to use Mirage?