Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If html2canvas() captures your div but leaves its images blank, start by checking the image request and its origin. Cross-origin images are the most common cause: html2canvas defaults to allowTaint: false, and useCORS: true works only when the image server returns a suitable Access-Control-Allow-Origin header. If you cannot change that server, serve the image from the page’s origin or use a controlled proxy.

Why html2canvas leaves images out

html2canvas does not take a native screenshot of the browser’s pixels. It clones the selected DOM, reads styles and resources, and reconstructs the result on a canvas. Images therefore have to load successfully and be usable under browser security rules. The library’s FAQ explains that every CSS property must be implemented manually, so it cannot provide complete CSS support.

Three different failures can look identical in the output:

  • Origin policy: the image is on another origin and would taint the canvas.
  • Loading failure: the URL returns an error, redirects unexpectedly, requires authentication, or is too slow.
  • Clone or rendering limitation: the image loads, but the cloned DOM, computed style, or a CSS feature is not supported as expected.

Find which case you have before changing options. An option cannot repair a 404 response or grant permission that the image server did not provide.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Fix it in a reliable order

1. Confirm that you are capturing the intended element

Pass the actual element, not a selector string or a wrapper that is empty at capture time. The official usage pattern is:

const target = document.querySelector('#capture');
const canvas = await html2canvas(target);

Check that target is not null, that it is visible, and that the image exists inside it:

const target = document.querySelector('#capture');
if (!target) throw new Error('Missing #capture element');

for (const img of target.querySelectorAll('img')) {
  console.log({ src: img.currentSrc || img.src, complete: img.complete, width: img.naturalWidth, height: img.naturalHeight });
}

naturalWidth of zero usually means the resource has not loaded or failed. If the markup is inserted asynchronously, wait until the application has added the images and they have finished loading before calling html2canvas.

2. Inspect the image request in DevTools

Open the browser’s Network panel, reload the page, and open each image request. Check its status, final URL after redirects, response headers, and whether it requires cookies or an authorization header. A failed request, a login redirect, or a blocked request must be fixed at the delivery layer; increasing a timeout will not make it valid.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use diagnostic logging while investigating:

const canvas = await html2canvas(document.querySelector('#capture'), {
  logging: true,
  imageTimeout: 15000,
  onclone: (clonedDocument) => {
    console.log('Images in clone:', clonedDocument.querySelectorAll('#capture img').length);
  },
});

imageTimeout is 15,000 milliseconds by default. Set a different value only when the Network panel shows a genuinely slow image. The onclone callback lets you inspect or adjust the cloned document without changing the live page.

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

3. Compare the page and image origins

An origin consists of scheme, host, and port. For example, an image at https://cdn.example.com/photo.jpg is cross-origin when the page is https://www.example.com, even though both names belong to the same organization.

html2canvas defaults allowTaint to false. It skips cross-origin images that would taint the canvas. A skipped image is expected behavior under that setting, not a rendering bug.

4. Try CORS only when the server supports it

For a remote host that you control, use:

const target = document.querySelector('#capture');
const canvas = await html2canvas(target, {
  useCORS: true,
  logging: true,
});

This tells the browser to make a CORS-aware image request. The image server must also return an appropriate Access-Control-Allow-Origin response header for your page’s origin (or a deliberately configured allowed origin). useCORS cannot add that header and cannot bypass the server’s policy. Inspect the image response in Network to verify the header and check the console for a CORS error.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If the page uses credentials, configure the server and request consistently with your authentication model. Do not expose private images with a permissive policy merely to make a screenshot work.

5. Serve the image same-origin

When you can move or mirror the asset, serving it from the same scheme, host, and port as the page avoids the cross-origin image restriction. Verify that the resulting URL is actually same-origin after redirects and that your server returns the correct content type. This is often simpler than maintaining a proxy, especially for a small set of static assets.

6. Use a controlled proxy when the source cannot send CORS

The html2canvas getting-started guidance documents a proxy option for loading cross-origin content through a proxy endpoint:

const canvas = await html2canvas(document.querySelector('#capture'), {
  proxy: 'https://your.example.com/html2canvas-proxy',
  logging: true,
});

The proxy must be yours (or operated under your organization’s control). Restrict it to approved hosts, validate URLs, limit response size and content types, apply timeouts, and prevent access to internal network addresses. A public arbitrary-URL proxy is not a safe default because it can become a server-side request-forgery and bandwidth-abuse service. The proxy should fetch the image and return it in a way that your page can use without violating the browser’s policy.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Images that are not ordinary <img> elements

CSS background images

A background-image is resolved through computed style rather than an HTMLImageElement. Inspect the captured clone and the computed value:

const node = document.querySelector('.hero');
console.log(getComputedStyle(node).backgroundImage);

Confirm that the URL is the expected one, that it loads in Network, and that its origin is permitted. A relative URL may resolve differently if the clone is created in a different document context. If the background uses gradients, masks, filters, or another CSS effect, remember that html2canvas implements CSS features individually and does not promise complete support.

Lazy-loaded and JavaScript-inserted images

Scroll or otherwise trigger the application’s lazy-loading logic, then wait for the src or srcset to be populated. Capture only after the image reports a nonzero naturalWidth. If the page replaces a placeholder in a framework effect, schedule the capture after that update has rendered.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

Authenticated and signed URLs

An image that works in a tab because of cookies may fail in the capture request, and a short-lived signed URL may expire during a delayed capture. Check the final request and authentication requirements. Do not place long-lived secrets in client-side markup solely for screenshotting; prefer a server-side, access-controlled delivery path.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why allowTaint: true is usually the wrong fix

You can set allowTaint: true, but that permits cross-origin content to taint the canvas. A tainted canvas cannot be read or exported with APIs such as toDataURL() and toBlob(). If your goal is a downloadable PNG, JPEG, or data URL, allowing taint does not solve the underlying problem. Use CORS, same-origin delivery, or a controlled proxy instead.

Diagnose the clone when loading succeeds

If the image request succeeds and has the required CORS response, inspect the cloned document with onclone. Look for selectors that hide the image, dimensions that collapse to zero, altered URLs, or a parent with clipping or unsupported styles. Temporarily remove animations, transitions, overlays, and lazy-loading attributes in the clone to isolate the issue:

const canvas = await html2canvas(document.querySelector('#capture'), {
  useCORS: true,
  onclone: (doc) => {
    doc.querySelectorAll('[data-hide-during-capture]').forEach((el) => el.remove());
    doc.querySelectorAll('img').forEach((img) => {
      img.loading = 'eager';
    });
  },
});

This changes only the cloned document. It cannot bypass browser security restrictions, repair a missing response header, or add support for an unimplemented CSS property.

Common symptoms and precise fixes

Symptom Likely cause Action
Only remote images are blank Cross-origin image with no permitted CORS response Configure Access-Control-Allow-Origin, serve same-origin, or use a controlled proxy; then set useCORS: true where appropriate.
Network status is 404, 403, or 5xx Bad URL, access control, or origin server failure Fix the URL or authorization and confirm the final response in Network.
Image appears intermittently Lazy loading, delayed insertion, or timeout Wait for the image to load; use logging and adjust imageTimeout only when evidence shows slow loading.
Request succeeds but CSS background is missing Computed URL, clone difference, or unsupported CSS Inspect computed style and the clone; simplify the CSS effect or use an <img> with a supported layout.
Canvas renders but export throws a security error The canvas is tainted Remove the taint source and use CORS, same-origin delivery, or a proxy. Do not rely on allowTaint for export.
Code fails in Node.js html2canvas depends on browser APIs and a browser document Run it in a browser environment, or use a browser-based screenshot service for server-side capture.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

For server-side captures, automated jobs, or pages whose assets are difficult to make same-origin, ScreenshotNeo provides a website screenshot API and MCP server. It accepts a URL and returns PNG, JPEG, WebP, or PDF. Before capture it can accept cookie/consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing with X-Page-Verdict and X-Billed headers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

One GET request is enough:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo documentation for request parameters. It also offers full-page captures with lazy images loaded, CSS-selector element capture, device and viewport controls, retina scale, custom CSS and JavaScript, click and wait actions, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage data, and an OpenAPI specification. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is included on every plan. Create a free ScreenshotNeo account to try it.

Final checklist

  • Verify the selected element and every image’s URL.
  • Use Network and console output to separate loading errors from CORS errors.
  • For cross-origin assets, configure the response header; useCORS alone is insufficient.
  • Prefer same-origin delivery or a security-reviewed proxy when you cannot change the image host.
  • Inspect computed styles and the cloned document for backgrounds, lazy loading, and unsupported CSS.
  • Keep allowTaint disabled when you need to export or read the canvas.

Frequently Asked Questions

Does adding crossorigin="anonymous" to an image fix the problem by itself?

No. It changes how the browser requests the image, but the image server must still return a compatible CORS response header. Verify the response in Network.

Can html2canvas capture an image that is behind a login?

Only if the capture request can load it with the required authentication and origin permissions. A successful view in your normal tab does not guarantee that the cloned capture request has the same credentials.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why does the image display in the page but disappear only in the canvas?

Normal display does not require the canvas to remain readable. A cross-origin image can render in the page yet be skipped or taint the canvas during reconstruction.

Is html2canvas a pixel-perfect screenshot tool?

No. It rebuilds the DOM and supported CSS in a canvas, so browser-native pixels and unsupported CSS effects may differ from what you see on screen.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.