/ โ† Image Optimisation Fixes / Check Lazy Loading
In this guide
  1. What working lazy loading looks like
  2. How far ahead browsers load lazy images
  3. How to test it in Chrome DevTools
  4. Making sure the first screen is not delayed
  5. Why a test fails, and what each result points to
  6. Platform notes
  7. The fix, step by step
  8. Edge cases worth knowing
  9. Keeping lazy loading correct
  10. Questions and answers

What working lazy loading looks like

Lazy loading means the browser holds back requests for images that are not near the visible part of the page, and fetches each one only as the visitor scrolls towards it. The aim is simple: spend the first seconds of a visit on the content people can actually see, and skip downloading images that many visitors never reach.

When it is working, a page load has a clear shape. The first batch of image requests covers the first screen plus a margin below it. Further requests then arrive in small groups as you scroll. When it is broken, you see one of two shapes instead: every image on the page requested at once, before you touch the scroll bar, or the main image near the top arriving late because it was told to wait.

Modern browsers support lazy loading natively through the loading="lazy" attribute on <img> and <iframe> elements. Older sites often use a JavaScript library instead, which keeps the real address in an attribute such as data-src and copies it into src when the image comes close. Both approaches can be tested the same way, by watching the network requests.

What you see on first loadWhat it means
Only the images on the first screen, plus a few belowLazy loading is working
Every image on the page, before any scrollingLazy loading is missing or being overridden
The hero image requested late, after scripts or other imagesThe hero image has been lazy-loaded by mistake
Placeholder images that never swap for real onesA lazy-load script is failing or blocked

How far ahead browsers load lazy images

Native lazy loading does not wait until an image is on screen. Each browser starts fetching a lazy image when it comes within a set distance of the viewport, so it has time to arrive before the visitor reaches it. That distance is why the first batch of requests includes images below the fold, and why a short page can look as if nothing is lazy at all.

Chrome uses a margin of about 1,250 pixels on fast connections and about 2,500 pixels on slower ones. Firefox and Safari use their own, generally smaller, margins. The exact numbers are browser internals and can change between versions, so treat them as a guide rather than a target.

ConditionEffect on the test
Fast desktop connectionImages within roughly one to two screens below the fold load straight away
Throttled or slow mobile connectionThe margin grows, so more images load on first view
Very tall viewport, such as a large monitorMore of the page counts as near the viewport
JavaScript switched offBrowsers treat lazy images as normal images and load them all

The last row surprises people. Browsers only defer lazy images when scripting is enabled, as a privacy measure, because scroll-based loading could otherwise be used to track how far someone reads. So if you test with JavaScript disabled, every image loads, and that is expected.

How to test it in Chrome DevTools

The Network panel gives you the clearest answer. Open DevTools with F12, or Cmd+Option+I on a Mac, and choose the Network tab. Click the Img filter so only image requests show, tick Disable cache, and make sure you are scrolled to the very top before reloading. If the page restores your old scroll position on reload, open it in a new tab instead.

Watch the request count at the bottom of the panel. Note the number when the page finishes loading, then scroll slowly. If lazy loading works, new rows appear as you go and the count climbs in steps. Right-click a column heading and turn on Priority and Initiator: lazy images are usually requested at Low priority, and the initiator shows whether the request came from the HTML parser or from a script.

Count lazy and eager images from the Console

Paste this into the Console on the page you are testing. It lists every image, whether it has the attribute, whether it has already been fetched, and how far down the page it sits.

const rows = [...document.images].map(img => ({
  src: (img.currentSrc || img.src || img.dataset.src || '').split('/').pop().slice(0, 40),
  loading: img.getAttribute('loading') || '(none)',
  loaded: img.complete && img.naturalWidth > 0,
  top: Math.round(img.getBoundingClientRect().top + scrollY)
}));
console.table(rows);
console.log('loaded on first view:', rows.filter(r => r.loaded).length, 'of', rows.length);

Run it once straight after a fresh load, without scrolling. Images far down the page, say with a top value above 3,000, should show as not loaded. If they are all loaded, the attribute is missing or something is overriding it.

Check the HTML the server sends

The Elements panel shows the page after scripts have run, which can hide where an attribute came from. To see what the server actually sends, fetch the page from the command line and count the attributes:

curl -s https://www.example.com/category/shoes/ | grep -o '<img[^>]*>' | head -20
curl -s https://www.example.com/category/shoes/ | grep -c 'loading="lazy"'
curl -s https://www.example.com/category/shoes/ | grep -c 'data-src='

If the server HTML has loading="lazy" but the browser loads every image, a script is setting the source early or replacing the markup. If the server HTML has no attribute at all, the theme or platform is not adding it, and the fix belongs in the template. Our image optimisation audit runs the same check across a whole page and flags each image that is eager when it could wait.

Making sure the first screen is not delayed

The more common mistake today is not too little lazy loading but too much. When a theme, plugin or component adds loading="lazy" to every image, the hero image, the logo and the first product photos are held back too. The browser does not know they are visible until layout is worked out, so their requests start later than they should. That delay feeds straight into Largest Contentful Paint.

In the Network panel, find your hero image and look at when its request started compared with the HTML document. If it starts after a run of scripts and stylesheets, or after smaller images lower down, it has been held back. In the Elements panel, select the image and read its attributes. The first-screen image should look like this:

<img src="/images/hero-1200.webp"
     srcset="/images/hero-800.webp 800w, /images/hero-1200.webp 1200w"
     sizes="100vw" width="1200" height="600"
     fetchpriority="high" alt="Garden studio with open doors">

No loading attribute at all is fine, because eager is the default. Adding fetchpriority="high" to the single most important image tells the browser to fetch it ahead of other images. Images further down keep loading="lazy" and should also carry width and height, so the space is reserved and nothing jumps when they arrive. See fixing missing image dimensions if they do not.

Lighthouse checks this for you. Depending on the version, it either shows an audit saying the Largest Contentful Paint image was lazily loaded, or an LCP request discovery insight that reports whether lazy loading was applied to the LCP image. Either warning means the attribute is on the wrong image. The full LCP picture is covered in our guide to fixing slow LCP.

โš ๏ธ Do not lazy-load the first image of a slider or carousel just because the other slides are hidden. The first slide is usually the LCP element. Lazy-load slides two onwards only.

Why a test fails, and what each result points to

SymptomLikely causeWhere to look
All images load on first viewNo loading attribute in the HTMLTheme template, page builder image settings, platform version
All images load, attribute presentA script copies data-src into src at once, or a slider preloads every slideLazy-load plugin settings, slider options, Initiator column
Images below the fold never appearLazy-load script fails, is blocked by another script, or is deferred until interactionConsole errors, script optimisation settings
Background images load eagerlyCSS background-image cannot take the attributeStylesheets and inline styles
Hero image arrives lateLazy attribute on the first imageHero block settings, template, plugin exclusions
Two copies of each image loadA native attribute and a script both handle the imageRemove one of the two methods

CSS background images deserve a note. The loading attribute only works on image and iframe elements, so a hero or section background set in CSS is fetched as soon as the stylesheet applies to an element on the page. If a long page uses many decorative backgrounds, either swap them for real images with the attribute, or use a small script that adds the background class when the section comes into view.

Duplicate handling is another common one. A site that moved to native lazy loading but kept an old script can show each image twice in the Network panel. Pick one method. Native lazy loading is now supported in Chrome, Edge, Firefox and Safari, so for most sites the script can go. Our guide to loading lazy not working covers each of these fixes in detail.

Platform notes

WordPress

WordPress has added loading="lazy" to content images since version 5.5. Since 5.9 it skips the first content image, and since 6.3 it also tries to add fetchpriority="high" to the image most likely to be the LCP element. Optimisation plugins often add their own lazy loading on top, with settings to exclude a number of leading images or images by class. If your hero is set by a page builder as a background, it will not be affected by any of this, so test it separately. More detail is in fixing images on WordPress.

Shopify

Most current Shopify themes use the image_tag Liquid filter, which accepts a loading value. Themes usually set it to lazy for everything except the first section. Check the theme's section files if collection pages load all product images at once.

{{ product.featured_image | image_url: width: 600 | image_tag: loading: 'lazy', sizes: '(min-width: 990px) 25vw, 50vw' }}

Next.js

The next/image component lazy-loads by default. The main above-the-fold image should be marked with the priority prop, or the newer preload prop in recent versions, which removes lazy loading and adds a preload hint. If every image on a page has that prop, nothing is lazy any more.

The fix, step by step

Step 1

Pick a long test page

Choose a page with at least 20 images and four or more screens of content, such as a category listing or blog archive. Open it in Chrome in a normal window at a typical laptop size. A short page is not a fair test, because the browser loads images within a margin below the visible area, so most of them may load straight away even when lazy loading is set up correctly.

Step 2

Set up the Network panel

Open DevTools, choose the Network tab, click the Img filter and tick Disable cache. Right-click a column heading and add Priority and Initiator. Scroll to the very top of the page, or open it in a new tab so the browser does not restore an old scroll position. You now have a clean view of every image request, its priority, and whether the HTML or a script started it.

Step 3

Reload and count

Reload the page without scrolling and wait for it to finish. Note the number of image requests shown at the bottom of the panel. On a page with 60 images, a working setup usually shows somewhere between 6 and 15. If the count matches the total number of images on the page, lazy loading is not working, and you should move on to checking the HTML attributes before going further.

Step 4

Scroll and watch

Scroll slowly down the page and watch the request list. New image rows should appear in small groups as you approach each section, and the count should climb in steps. If nothing new appears because everything was already loaded, the images are eager. If images never appear and placeholders stay grey, a lazy-load script is failing, so open the Console and look for errors.

Step 5

Check the attributes

Select a lower image in the Elements panel and confirm it has loading="lazy", or the data-src attribute your lazy-load script uses. Then run curl against the page and count loading="lazy" in the raw HTML. If the attribute is in the browser but not the server HTML, a script adds it. If it is in neither, the template needs changing. Confirm the change by repeating the count.

Step 6

Protect the first screen

Select the hero image, logo and first row of products and confirm none of them carry loading="lazy". Remove it from the template or exclude them in your plugin settings, and add fetchpriority="high" to the single main image. Reload and check in the Network panel that the hero request now starts early, near the top of the waterfall, rather than after scripts and other images.

Step 7

Run Lighthouse

Open the Lighthouse panel, choose Mobile and Performance, and run a report. Look for a warning that the LCP image was lazily loaded, or the LCP request discovery insight saying lazy loading was applied to it. Also look for offscreen image advice listing images that could wait. Both lists should be empty once the fix is right. Note the LCP time so you can compare later.

Step 8

Repeat on a slow connection

Set the Network throttling menu to a slow mobile preset and repeat the reload and scroll test. More images will load on first view, because browsers widen the loading margin on slow connections, but you should still see requests arriving in stages as you scroll. If the slow test shows every image loading at once while the fast test does not, check for a script that changes behaviour by connection.

Step 9

Record the result

Write down the page, the first-load image count, the hero request start time and the Lighthouse LCP figure. Repeat the same test after any theme, plugin or template update. A simple note like 8 of 60 images on load, hero at 120 ms makes regressions obvious, and gives you a baseline to show a developer or client if something changes later without warning.

Edge cases worth knowing

Keeping lazy loading correct

Lazy loading tends to break quietly, after a theme update, a new page builder block or a change of optimisation plugin. Build a short check into your routine:

If LCP worsens after a change, check lazy loading first. It is one of the quickest regressions to find and one of the quickest to undo. The Core Web Vitals assessment guide explains how to read the field data once the fix is live.

Questions and answers

1. How do I know if lazy loading is working on my site?

Open Chrome DevTools, choose the Network tab, filter by Img and tick Disable cache, then reload a long page without scrolling. If only the images near the top load at first and new requests appear as you scroll, lazy loading is working. If every image on the page is requested before you scroll, it is not. Test on a page with many images, such as a category listing, because short pages often load everything within the browser's normal loading margin.

2. Why do images below the fold still load straight away?

Browsers start loading lazy images before they reach the screen, so some images below the fold loading early is normal. Chrome uses a margin of roughly 1,250 pixels on fast connections and more on slow ones, so images within a screen or two of the fold are fetched on load. If images much further down also load, check that they carry the lazy attribute in the HTML and that no script or slider is forcing them to load early.

3. Should the hero image be lazy loaded?

No, the hero image and anything else visible on the first screen should load eagerly. Lazy loading delays the request until the browser has worked out the layout, which pushes back Largest Contentful Paint. Remove the lazy attribute from the hero, logo and first row of content, and add fetchpriority set to high on the single main image. Lighthouse warns when the LCP image is lazily loaded, so run it after the change to confirm the warning has gone.

4. Does lazy loading work in all browsers?

Native lazy loading with the loading attribute works in all current major browsers, including Chrome, Edge, Firefox and Safari, for images, and in most of them for iframes too. Browsers that do not support it simply ignore the attribute and load the image normally, so nothing breaks. Each browser uses its own distance for when to start loading, so the exact number of images fetched on first view varies between them, which is expected and not a fault.

5. Why does Lighthouse still show offscreen image warnings?

Lighthouse flags images below the fold that were downloaded during page load and could have waited. The usual causes are images without the lazy attribute, CSS background images, which cannot use the attribute, and images loaded by a slider or gallery script. Open the list in the report, check each image in the Elements panel, and add the attribute in the template or switch the plugin setting on. Some Lighthouse versions present this advice inside image delivery insights instead.

6. Can Google see images that are lazy loaded?

Yes, Google can see images that use native lazy loading, because Googlebot renders pages with a tall viewport and loads the images it finds. Problems arise with older scripts that only load images when a real scroll or click happens, because Googlebot does not scroll like a person. If you use a script, make sure the real image address is in the HTML or appears without user interaction, and check a page with the URL Inspection tool in Search Console.

7. Why does lazy loading not work with JavaScript disabled?

Browsers deliberately load all images normally when JavaScript is switched off, as a privacy protection. Without that rule, a site could use the timing of lazy image requests to track how far a visitor scrolls even when scripts are blocked. So if you test with JavaScript disabled and every image loads, that is expected behaviour and not a fault in your site. Always test lazy loading with JavaScript enabled and a normal browser profile.

Check every image on your page for lazy loading

Our image optimisation audit lists each image on a page, shows which ones load eagerly when they could wait, and flags a lazy-loaded hero image that is slowing your LCP.

Open Image Optimisation Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All Image Optimisation Fixes  ·  How to Fix Loading Lazy Not Working  ·  How to Fix Image Lazy Loading  ·  How to Fix Slow LCP  ·  How to Fix Images on WordPress  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.