What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Use useCORS: true only when the image server sends a suitable Access-Control-Allow-Origin header. If you cannot change that server, load the image through a same-origin proxy. Keep allowTaint disabled when you need to export the canvas: allowing a tainted canvas does not make it readable.
html2canvas does not take a photograph of the browser window. It walks the DOM and paints a canvas from the elements and CSS properties it understands. That distinction matters for remote images: an image can be visible in the page while still being forbidden from canvas readback by the browser’s same-origin policy.
The reliable choices are therefore limited:
- Configure the image host to allow your page origin, then use
useCORS: true. - Fetch the image through infrastructure you control and serve it from your own origin, then configure html2canvas with
proxy.
There is no html2canvas flag that overrides browser content policy. The project’s documentation states: “html2canvas cannot circumvent browser content policy restrictions.”
Why an external image can be visible but unavailable to canvas
Browsers define an origin by scheme, host and port. An image at another origin may render normally in an <img> element, yet drawing it into a canvas can make that canvas tainted. A tainted canvas cannot be inspected or exported with APIs such as toDataURL().
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →#1 Best Overall
html2canvas normally avoids images it expects would taint the result. Its documented defaults are:
| Option | Default | Meaning |
|---|---|---|
useCORS |
false |
Do not attempt a CORS image request unless enabled. |
allowTaint |
false |
Do not draw images that would taint the canvas. |
proxy |
null |
No proxy URL is used. |
These defaults are version-sensitive, so check the configuration reference for the html2canvas release installed in your project.
Route 1: enable CORS on the image host
Choose this route when you control the image server, or when its operator already permits requests from your application. The server must return an appropriate Access-Control-Allow-Origin response header; setting a client option cannot create that permission.
Minimal html2canvas call
const element = document.querySelector('#invoice');
const canvas = await html2canvas(element, {
useCORS: true,
allowTaint: false
});
const png = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.download = 'invoice.png';
link.href = png;
link.click();
Use the same option when you need a JPEG or another canvas export method. The important requirement is that every remote image included in the rendered subtree is loaded with a response the browser accepts for CORS.
Make the image request CORS-aware
If you create or replace an image element yourself, set its crossOrigin property before assigning src:
const image = new Image();
image.crossOrigin = 'anonymous';
image.src = 'https://images.example.test/photo.jpg';
await image.decode();
document.querySelector('#preview').append(image);
The property does not grant access by itself. The response still needs a matching Access-Control-Allow-Origin header, usually naming your page’s origin or using a policy appropriate for the asset.
Rank #2
Verify the actual response
- Open browser developer tools and inspect the image request in the Network panel.
- Confirm that the request URL is the one you expect, including redirects.
- Inspect the response headers for
Access-Control-Allow-Origin. - Render the element again and test the export operation.
A header on your HTML page or on a different CDN path does not fix the image response. Redirects, alternate hosts and signed URLs can cause the final response to have different headers.
Route 2: use a same-origin image proxy
Use a proxy when the external image host cannot be configured for your required CORS policy. Your application retrieves the image, then exposes it to the browser through the same origin as the page. html2canvas’s getting-started documentation demonstrates configuring a proxy URL and an endpoint that accepts a ?url= query and returns the resource as a base64 data URI.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Client configuration
const canvas = await html2canvas(document.querySelector('#invoice'), {
proxy: '/image-proxy',
allowTaint: false
});
const png = canvas.toDataURL('image/png');
The endpoint above is illustrative. Implement and host it as part of your application; do not assume that an arbitrary public proxy is trustworthy or officially supported.
What the proxy must do
- Accept an image URL from your application.
- Retrieve the resource server-side.
- Validate the response and content type.
- Return the bytes (or the data-URI form expected by your implementation) from your own origin.
- Preserve a correct image media type so the browser can decode it.
Because the proxy becomes part of your production infrastructure, define an allowlist of hosts or URL patterns, enforce request limits, handle timeouts, and decide how to cache responses. Without those controls, a URL-fetching endpoint can be abused to request internal services or consume excessive bandwidth. The html2canvas documentation describes the architectural route but does not prescribe your access-control, rate-limit or caching policy.
Illustrative Express endpoint
The following is a starting point, not a drop-in security policy. It deliberately restricts hosts and limits the response size; adapt it to your deployment and authentication model.
import express from 'express';
const app = express();
const allowedHosts = new Set(['images.example.test']);
app.get('/image-proxy', async (req, res) => {
try {
const target = new URL(String(req.query.url || ''));
if (target.protocol !== 'https:' || !allowedHosts.has(target.hostname)) {
return res.status(400).send('Image host is not allowed');
}
const upstream = await fetch(target, { redirect: 'follow' });
if (!upstream.ok) return res.status(502).send('Image request failed');
const type = upstream.headers.get('content-type') || '';
if (!type.startsWith('image/')) return res.status(415).send('Not an image');
const data = Buffer.from(await upstream.arrayBuffer());
if (data.byteLength > 10 * 1024 * 1024) {
return res.status(413).send('Image is too large');
}
res.set('Content-Type', type);
res.set('Cache-Control', 'private, max-age=300');
res.send(data);
} catch {
res.status(400).send('Invalid image URL');
}
});
app.listen(3000);
Point proxy at this route only after adding the controls your application needs. If your proxy returns a data URI rather than raw bytes, follow the format used by the html2canvas getting-started example.
Rank #3
Why allowTaint: true does not solve exports
allowTaint controls whether html2canvas may draw an image that would taint the canvas. It does not make the canvas readable. If any image taints the result, calls such as canvas.toDataURL('image/png') can fail with a security exception. Leave it false whenever the result must be downloaded, uploaded or inspected.
Also check for canvases already present inside the element. A pre-existing tainted canvas can contaminate the new rendering even when the external image you are investigating has correct headers.
Complete browser example with a fallback decision
This example tries the direct CORS route and reports a useful error instead of silently producing an incomplete image:
async function capture(selector) {
const element = document.querySelector(selector);
if (!element) throw new Error(`No element matches ${selector}`);
const canvas = await html2canvas(element, {
useCORS: true,
allowTaint: false,
imageTimeout: 15000
});
try {
return canvas.toDataURL('image/png');
} catch (error) {
throw new Error(
'The canvas is not readable. Check every remote image response for CORS headers, or use a same-origin proxy.'
);
}
}
capture('#invoice')
.then(dataUrl => {
const link = document.createElement('a');
link.download = 'invoice.png';
link.href = dataUrl;
link.click();
})
.catch(console.error);
imageTimeout is an application-level example; tune it for your page and installed html2canvas version. A timeout does not repair a CORS violation.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsTroubleshooting: symptom, cause and fix
| Symptom | Likely cause | Fix |
|---|---|---|
| Remote image is missing from the capture | The image would taint the canvas and useCORS is off, or the CORS request was rejected. |
Inspect the final image response. Enable useCORS: true only after the host sends the required header, or route the image through your proxy. |
useCORS is enabled but nothing changes |
The external server did not grant permission, or a redirect leads to a response without the header. | Check the Network panel, redirects and the exact requested URL. Client JavaScript cannot alter that response policy. |
Capture appears complete but toDataURL fails |
Some image or nested canvas tainted the result. | Keep allowTaint: false, remove or proxy the offending resource, and test nested canvases separately. |
| Proxy requests return errors | Invalid URL, disallowed host, upstream timeout, non-image content or an oversized response. | Log the upstream status and content type, validate URLs, allow only required hosts and return clear 4xx/5xx responses. |
| Layout differs from the browser | html2canvas reconstructs the DOM and supports only the CSS properties it understands. | Reduce unsupported styling, wait until fonts and images are ready, and treat the output as a rendered representation rather than a pixel-identical screenshot. |
Reliability and performance considerations
Prefer direct CORS when you control the host
A direct request avoids a proxy hop and keeps the image on its normal delivery path. It is the simplest operational model, but every image origin in the captured subtree must cooperate.
Use a proxy for consistency
A proxy gives your application one policy boundary when images come from vendors you cannot configure. It also introduces server bandwidth, timeout and caching decisions. Cache only content your application is allowed to store, and avoid forwarding user-controlled credentials to arbitrary hosts.
Rank #4
Capture after resources are ready
Run html2canvas after the target element is present and after required images have loaded. For dynamically inserted images, wait for their load or decode() completion before capturing. Lazy-loaded images may otherwise be absent simply because they were never requested.
Remember html2canvas’s rendering scope
The library does not capture browser chrome, cross-origin content that policy blocks, or every CSS feature exactly as a browser compositor does. If you need a server-rendered page image or PDF without wiring a browser and proxy, a hosted capture service may be a better fit.
Recommended Free Tools
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. One request can return a PNG, JPEG, WebP or PDF, so your application does not need to configure html2canvas, CORS headers or an image proxy for a remote page.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for request options and response details. The equivalent Python and Node.js calls are:
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie and consent banners, newsletter popups and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, failed loads and cache hits are not billed. Response headers identify the page verdict and billing result with
X-Page-VerdictandX-Billed. - An MCP server provides
take_screenshot,get_page_infoandcapture_pdftools for Claude, Cursor and other MCP clients. - The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; yearly billing provides two months free, and every feature is included on every plan.
When your goal is a clean page capture rather than a DOM-rendered canvas, sign up for the free ScreenshotNeo plan and start without a card.
FAQ
Can I capture an image from another subdomain without a proxy?
Only if the image response is CORS-permitted for the page origin. A different subdomain is still a different origin when the host, scheme or port differs.
Free tools Windows power users keep installed
One-click scans. No signup required.
Best Value
Does converting the image URL to a data URI in the browser bypass CORS?
No. The browser must first obtain the bytes. Fetching them from an origin that has not granted access is subject to the same policy.
What should I test when only one image fails?
Inspect that image’s final request and response independently. Mixed image hosts often have different headers, redirects or authentication requirements.
Is html2canvas suitable for a pixel-perfect archival screenshot?
Not always. It reconstructs a representation from DOM information and supported CSS, so browser rendering differences and unsupported properties can remain even after CORS is configured.
Frequently Asked Questions
Can I capture an image from another subdomain without a proxy?
Only when the image response grants CORS access to the page origin; a different subdomain is still a different origin.
Does converting the image URL to a data URI in the browser bypass CORS?
No. Obtaining the bytes is still subject to the browser’s cross-origin policy.
What should I test when only one image fails?
Inspect that image’s final request and response separately, including redirects and response headers.
Is html2canvas suitable for a pixel-perfect archival screenshot?
Not always; it rebuilds the DOM using supported CSS rather than capturing the compositor output exactly.
Quick Recap
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.

