ScreenshotNeo

BlogHow-to

What to Do When WordPress Lazy-Loading Images Don’t Appear in a Screenshot

Find out whether WordPress failed to load an image or your screenshot method missed it, then fix the cause without disabling lazy loading site-wide.

By the ScreenshotNeo team4 October 20269 min read

If a WordPress image is missing from a screenshot, first check whether it appears on the published page in a normal browser. Scroll until the image is visible and wait for it to load, then capture again. If the image appears live but not in the screenshot, the capture method or its timing is the likely issue. If it is missing live too, inspect the rendered image URL and the browser’s Console and Network panels before changing lazy-load settings.

WordPress has used the standard HTML loading attribute for native image lazy loading since WordPress 5.5. It defers offscreen image requests; it does not remove the image. The troubleshooting steps below separate a page-loading failure from a capture-only failure. WordPress’s developer note on lazy loading describes the core behavior.

1. Check whether the live page loads the image

  1. Open the published page in a regular browser. Use the same URL and, if relevant, the same login state as the screenshot request.
  2. Scroll until the missing image is in view. Wait for it to appear. Reload the page and repeat if needed.
  3. Compare the result with the screenshot. If scrolling makes the image appear and the later screenshot includes it, capture timing or deferred loading was the cause.
  4. If the image stays missing in the browser, open Developer Tools. Inspect the rendered <img> element and its src, srcset, and sizes attributes. In Network, filter to images and check whether the expected request was sent and whether it succeeded. Review Console for JavaScript errors.

Test the published page rather than relying only on the WordPress editor preview: themes, plugins, caching, and optimization can change the final markup or behavior. Google’s guidance for fixing lazy-loaded content recommends checking the rendered page and making important content load when it becomes visible without requiring a click or other interaction.

2. Use the result to identify the failure class

What you observe Likely area to investigate Next check
Image appears after scrolling; the screenshot was taken before that. Capture started before the deferred image loaded. Wait for the image or a selector that represents it, then capture.
Image appears in the live browser, but not in a DOM-rendered screenshot. Capture implementation, cross-origin image restrictions, or capture timing. Compare with a browser-native screenshot; if using html2canvas, check CORS and its options.
Image does not appear live, even after scrolling. Broken URL, server response, blocked request, markup, or JavaScript. Inspect rendered attributes, Network status, and Console.
Only the hero or another initially visible image is late or missing. That image may have been marked lazy when it should load eagerly. Inspect its loading attribute and how the theme renders it.

Do not assume every screenshot tool behaves like html2canvas. Its documentation specifically explains that it builds a representation from DOM information rather than taking the browser’s own screenshot, and that cross-origin images can be skipped under canvas security rules. That limitation applies when using that library; other capture methods have their own behavior.

3. Fix capture timing for deferred images

A browser screenshot should be taken after the target image has loaded. A fixed delay can help diagnose a timing issue, but it is less reliable than waiting for a specific image or selector: network speed and image size vary. For a page with many offscreen images, scroll through the page in steps and wait for each region before taking a full-page capture if your capture method does not load lazy images itself.

For a manual check, this browser-console snippet scrolls through the page in viewport-sized steps, pauses briefly at each step, then returns to the top. It is a diagnostic aid, not a guarantee that every site’s custom lazy loader has finished:

async function warmLazyImages() {
  const step = Math.max(1, window.innerHeight * 0.8);
  for (let y = 0; y < document.documentElement.scrollHeight; y += step) {
    window.scrollTo(0, y);
    await new Promise(resolve => setTimeout(resolve, 400));
  }
  window.scrollTo(0, 0);
  await new Promise(resolve => setTimeout(resolve, 500));
}

await warmLazyImages();

For automated browser capture, prefer waiting for a meaningful selector or image readiness condition where your tool supports it. A generic “network idle” condition can be unsuitable on pages with analytics, polling, or other ongoing requests. If the page inserts images only after scrolling, the capture needs to trigger that behavior before waiting for completion.

4. Check CORS when using html2canvas

When a browser draws a cross-origin image to a canvas without the required CORS permission, the canvas may be tainted or the renderer may omit that image. If the missing image is hosted on a different origin (including a CDN or a different subdomain), inspect the image response headers. The image server must permit the requesting origin for a CORS-enabled canvas capture. You cannot solve a server-side CORS restriction by changing WordPress’s loading attribute.

For html2canvas, try useCORS: true only when the image host sends an appropriate Access-Control-Allow-Origin response header. A same-origin proxy is another documented option when you control and can safely operate one. Avoid enabling allowTaint as a supposed fix if you need to read or export the canvas; a tainted canvas cannot be exported normally.

import html2canvas from 'html2canvas';

const target = document.querySelector('#page-to-capture');
if (!target) throw new Error('Capture target not found');

const canvas = await html2canvas(target, {
  useCORS: true,
  backgroundColor: '#ffffff',
  logging: true
});

document.body.appendChild(canvas);

This example assumes the package is installed and the code runs in a browser bundler. Replace #page-to-capture with an element that contains the content to capture. useCORS does not bypass browser security or make a non-CORS-enabled host accessible. See the html2canvas documentation and its FAQ for renderer and cross-origin details.

5. Inspect WordPress output and lazy-load settings

WordPress adds loading optimization attributes during output generation, and themes and plugins can also rewrite image markup or implement their own lazy loading. Inspect the final rendered HTML, not just the media library entry or editor settings. Check whether the missing image has a usable src or responsive source, and whether another optimization plugin is changing the markup.

For a template image rendered with wp_get_attachment_image(), pass an eager loading value for an image that belongs in the initial viewport:

<?php
$hero_id = get_post_thumbnail_id();

if ( $hero_id ) {
    echo wp_get_attachment_image(
        $hero_id,
        'large',
        false,
        array( 'loading' => 'eager' )
    );
}
?>

Use this only for a known above-the-fold image such as a hero or featured image that is actually visible on initial load. Do not make every image eager to compensate for a screenshot that runs too early. Images below the fold are good candidates for deferred loading. Chrome’s guidance advises against lazy-loading the likely Largest Contentful Paint image; see Chrome’s LCP discovery guidance.

WordPress exposes filters for more specific behavior. The wp_lazy_loading_enabled() reference describes a broad control over whether the attribute is added to a tag and context. For content images, WordPress’s developer note documents wp_img_tag_add_loading_attr, which can make a targeted decision for an individual image. Prefer a narrowly scoped rule that identifies the intended image; avoid globally disabling lazy loading as a first response.

6. Troubleshoot common errors

Symptom or error Cause to check Fix
src is empty, invalid, or points to an old file. Broken markup, stale cached HTML, or an incorrect image URL. Correct the source in the content or theme; purge relevant page/CDN caches and inspect the newly rendered markup.
Image request returns 404 or another failed status. Missing file, wrong generated image size, or stale URL. Verify the exact URL directly, check the media file and responsive variants, then update the URL or regenerate the correct asset using your normal WordPress workflow.
Image request is blocked or reports a CORS error. Cross-origin host does not grant access to the capture origin. Configure the image host’s CORS response if you control it, use a permitted same-origin proxy, or use a browser-level capture path that does not need canvas access.
Image appears only after manual scrolling. Lazy-load trigger has not run before capture. Scroll the capture page, wait for the image or its selector, then capture.
Image is visible in the browser but absent from html2canvas. DOM reconstruction or canvas restrictions rather than WordPress loading. Check resource origin and CORS response, enable useCORS when supported by the host, or compare against a native browser capture.
Hero image alone loads late. Initial-viewport image may be lazy-loaded or deprioritized. Inspect its final HTML and render it eagerly when it is the page’s visible hero/LCP image.
Image works for logged-in users but not in the capture. Different cookies, authorization, or personalized output. Compare the public page in a logged-out browser and provide the capture environment the required access only through a secure, supported mechanism.
Changes in WordPress do not affect the screenshot. Cached page, CDN, plugin optimization, or screenshot cache. Clear the relevant cache layers, confirm the live HTML changed, and repeat with a fresh capture.

7. Keep captures reliable and efficient

  • Wait on a condition, not an arbitrary long sleep. Waiting for the target image or a page-specific ready marker is usually more repeatable than guessing a delay. Keep a timeout so a broken page cannot stall a job forever.
  • Trigger lazy loading deliberately. Full-page capture behavior varies. Some browser-level tools load lazy images as they expand the viewport; others capture before that happens. Confirm behavior for your specific tool and page.
  • Keep the hero eager and lower images lazy. This preserves the intended loading optimization while helping the initial visual content render promptly.
  • Check representative page types. A post body, archive grid, custom theme hero, and third-party image CDN may produce different markup and loading behavior.
  • Retry selectively. Retry transient timeouts or failed requests with a limit. Repeatedly retrying a permanent 404, invalid URL, or CORS denial adds work without fixing the cause.

Lazy loading can reduce unnecessary image downloads for visitors who never scroll to lower content, while eager-loading too many images can increase initial network work. For screenshot jobs, extra scrolling and waiting add capture time; target-aware waits and selective retries help avoid paying that cost on every page. The dossier provides no benchmark or universal delay that fits every WordPress site, so measure against your own pages and capture environment.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request can return a screenshot; its full-page capture loads lazy images. Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed, and responses include page-verdict and billing headers. AI agents can use its MCP server tools to take screenshots, get page information, and capture PDFs.

See the ScreenshotNeo API documentation for options and response details. Example using cURL:

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://your-wordpress-site.example/article \
  -o shot.webp

Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={
        "access_key": "YOUR_API_KEY",
        "url": "https://your-wordpress-site.example/article",
    },
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://your-wordpress-site.example/article'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

The Node.js example uses Bun’s file writer; with Node.js, save the response body using your preferred filesystem method. Free includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots; yearly billing gives two months free, and every feature is on every plan. Sign up for 1,000 free screenshots a month, no card required.

FAQ

Does loading="lazy" mean the image will not appear in a full-page screenshot?

No. It means the browser can defer fetching an offscreen image until it is near the viewport. Whether a full-page capture triggers that load depends on the capture method.

Should I disable lazy loading across my WordPress site?

Usually not. First identify whether the live page is broken or only the capture is missing the image. Make an initially visible hero eager when appropriate; retain lazy loading for offscreen images.

Can I fix a CORS error in WordPress?

Only if you control the image host or can arrange an allowed proxy. WordPress’s loading attribute does not grant cross-origin canvas permission.

Why does the image appear after I scroll?

The image was likely deferred until it approached the viewport, or a theme/plugin’s visibility-triggered loader had not run before the earlier capture.