How to check if images are lazy loaded

Fetch the served HTML and count the <img> tags against the loading="lazy" attributes, then confirm in the browser that images below the fold hold their requests until you scroll. The markup count and the rendered count differ whenever script inserts images after the load.

Why check this

Lazy loading is a two-sided setting, and both sides break releases. Missing it on a long page pulls dozens of images into the first load. Applying it to the image at the top of the viewport delays the largest paint instead. Run this check after any template change that touches image markup, and before sign-off on any page with a gallery, a feed or a long article.

Prerequisites

Steps

  1. Step 1.

    Count the image tags in the served markup and how many of them carry the attribute.

    curl -s https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS | grep -o '<img[^>]*>' | awk '{n++; if ($0 ~ /loading="lazy"/) lazy++} END {printf "img %d\nlazy %d\nmissing %d\n", n, lazy, n-lazy}'
    
    img 4
    lazy 4
    missing 0

    This is the pre-filter. It answers one question: does the server send the attribute. It cannot tell you whether the browser honoured it.

  2. Step 2.

    List the tags one by one, so a single missing attribute names its own file.

    node -e "const html = await (await fetch('https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS')).text(); const imgs = html.match(/<img[^>]*>/g) || []; for (const tag of imgs) { const loading = (tag.match(/loading=\"([^\"]+)\"/) || [])[1] || 'NONE'; console.log(loading.padEnd(6), (tag.match(/src=\"[^\"]*\/([^\"\/]+)\"/) || [])[1]); } console.log('img', imgs.length, '| lazy', imgs.filter(t => /loading=\"lazy\"/.test(t)).length);"
    
    lazy   fetching-page-cors.svg
    lazy   simple-request.svg
    lazy   preflight-correct.svg
    lazy   include-credentials.svg
    img 4 | lazy 4
  3. Step 3.

    Open the URL in Chrome, press F12 and read the same counts from the rendered document rather than from the markup.

    <img> elements       4
    loading="lazy"       4
    no loading attribute 0

    Both counts agree on this page. On a page that builds its gallery in script, they will not, and the rendered count is the one that matters.

  4. Step 4.

    Confirm the deferral is real. Open the Network panel, filter to Img, tick Disable cache, reload without scrolling, and count the image requests. Scroll to the bottom and count again. Images that only appear in the second count are the ones being deferred.

How to read the result

| What you see | What it means | What to do | | --- | --- | --- | | missing 0 in step 1 | Every image in the served markup carries the attribute | Check step 4 next. Presence is not behaviour. | | missing above zero | Some images load with the first request | Confirm they are above the fold before filing anything. | | The markup count is below the DOM count | Script adds images after the load | Test with the DOM count, and check how the script sets the attribute. | | Every image requested before you scroll | Nothing is being deferred | Look for a script that rewrites the attribute, or a viewport tall enough to contain them all. | | The top image is lazy | The largest paint waits for layout before its fetch starts | Remove the attribute from that one image. |

Common mistakes

Sign: The attribute is on every image and the browser still requests them all at once.Cause: Native lazy loading defers only images far enough from the viewport, and the distance threshold depends on viewport height and connection type. An image a little below the fold is fetched immediately by design, which is not a bug to file.
Sign: A markup scan reports zero lazy images on a page that clearly defers them.Cause: Some sites defer with IntersectionObserver and a data-src attribute, with no native attribute anywhere. Search the markup for data-src and for a placeholder src before opening a ticket.
Sign: Lazy loading was added and the LCP got worse.Cause: The attribute on an in-viewport image holds the request until layout tells the browser the image is visible. That delay lands inside the LCP load delay subpart. The largest element in the first viewport is never lazy.
Sign: The count in the HTML and the count in DevTools disagree.Cause: curl reads what the server sent. Images a framework adds after hydration are absent from that document. Both counts were 4 on the captured page, so a difference on yours points at client-side rendering, not at a scan error.

What to check next

FAQ

How to check if images are lazy loaded without DevTools?

Step 1 is the answer for native lazy loading. It reads the attribute straight from the served HTML and needs no browser. What it cannot show is whether the browser deferred the request, which needs the Network panel in step 4.

Should every image be lazy loaded?

No. Images inside the first viewport should load with the page, and the largest of them decides LCP. Apply the attribute from the first image below the fold onward.

How do I tell native lazy loading from a script-based one?

Native lazy loading is the loading="lazy" attribute in the markup. A script-based one usually leaves src empty or pointing at a placeholder and keeps the real URL in data-src. The two behave differently with JavaScript disabled.

Does lazy loading reduce page weight?

It reduces the weight of the first load, not the total. A reader who scrolls to the end downloads the same bytes. The gain is in what the first paint has to compete with.

Verified

The rendered counts in step 3 come from one Chrome DevTools capture of https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS on 2026-09-11, on one machine. The two command steps were run against the same URL on the same day. Read your own Chrome build from chrome://version and record it with your own numbers.

Verified by Maks Vernycurl 8.21.0node 22.23.2

Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.

basic6 minpublished updated Maks Verny