Configure CORS in the S3 bucket and enable useCORS: true in html2canvas. The bucket must return an Access-Control-Allow-Origin value that matches the page’s origin; useCORS cannot bypass a browser decision. If you cannot change the bucket, fetch the image through a tightly controlled same-origin proxy instead.
Why an S3 image taints an html2canvas canvas
A page origin is the combination of its scheme, host, and port. For example, https://app.example.com and https://images.example.com are different origins, as are http://localhost:3000 and http://localhost:5173. An S3 object URL is normally another origin again.
When JavaScript draws a cross-origin image onto a canvas, the browser checks whether the image response authorizes the page origin with CORS. Without that approval, the canvas is tainted. A tainted canvas may still display pixels, but operations that read pixels—such as toDataURL(), toBlob(), or getImageData()—raise a security exception. html2canvas is subject to this browser rule; it cannot grant access that the browser denied.
useCORSdefaults tofalse. Setting it totruetells html2canvas to attempt a CORS-enabled image request; it does not add a response header to S3.allowTaintdefaults tofalse. Changing it totruecan allow a tainted image to be drawn, but the resulting canvas remains unreadable, so it is not a solution when you need an exported screenshot.- Public-read access and CORS are separate. An object can load in an
<img>element while still being unavailable to canvas scripts. - Every resource drawn into the canvas must pass the same checks. One non-CORS image, SVG, font, iframe, or pre-tainted canvas can make the final canvas unusable.
Diagnose the failing request first
- Open browser developer tools, select the Network panel, and reload the page.
- Find the S3 image request used by the element being captured. Inspect the request’s
Originheader. It should be the exact origin of the page running html2canvas. - Inspect the final response, not just the URL in your source. Confirm that it contains
Access-Control-Allow-Originwith that exact origin, or an intentionally permitted wildcard for a non-credentialed design. - If an
OPTIONSrequest appears, inspect its response too. The S3 rule must allow the requested method and every requested preflight header. - Check redirects, CDN responses, signed URLs, and alternate hostnames. A redirect can land on a different origin, and the final response can have different headers from the original S3 URL.
If the image request itself is blocked, fix that request before changing html2canvas. If the image loads but canvas.toDataURL() fails, look for another cross-origin resource in the capture area.
Free tools Windows power users keep installed
One-click scans. No signup required.
#1 Best Overall
- Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
- Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
- Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
- Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
- 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.
Configure CORS on the S3 bucket
Use the exact page origin
In the Amazon S3 console, open the bucket, choose Permissions, and edit Cross-origin resource sharing (CORS). The console expects a JSON array. A narrow rule for one production site is:
[
{
"AllowedOrigins": ["https://app.example.com"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedHeaders": ["*"]
}
]
Replace https://app.example.com with the real scheme, host, and port of the page. Do not add a path: https://app.example.com/dashboard is not an origin. Add separate entries for development and production origins when required, for example a localhost origin and the deployed site. Keep the list as narrow as your application permits.
How S3 chooses a rule
S3 matches a CORS rule against the request origin, HTTP method, and requested headers. It uses the first matching rule. Therefore, include the method html2canvas uses (GET for the image; HEAD can be useful for metadata checks) and ensure that every header requested during preflight is covered by AllowedHeaders. A rule that matches the origin but omits the method or a requested header still fails.
Credentials and wildcards
The example uses an anonymous image request and an explicit origin. If your design sends credentials, the server must return a specific allowed origin rather than a wildcard and must apply the corresponding credential policy. Do not broaden the rule merely because the object is public.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteRank #2
- 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
- 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
- Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
- 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
- What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.
Load the image and capture it with html2canvas
Mark the image for a CORS load
When you control the markup, add crossorigin="anonymous" to the image. The S3 response must still authorize the page origin.
<img id="hero"
crossorigin="anonymous"
src="https://bucket.s3.amazonaws.com/path/image.jpg"
alt="">
Complete capture example
Wait for images in the capture area, request CORS in html2canvas, and leave allowTaint disabled so a failure is visible instead of producing an unreadable canvas:
async function capture() {
const target = document.querySelector('#capture');
if (!target) throw new Error('Capture target not found');
const images = [...target.querySelectorAll('img')];
await Promise.all(images.map((img) => {
if (img.complete) return Promise.resolve();
return new Promise((resolve) => {
img.addEventListener('load', resolve, { once: true });
img.addEventListener('error', resolve, { once: true });
});
}));
const canvas = await html2canvas(target, {
useCORS: true,
allowTaint: false,
imageTimeout: 15000
});
document.body.appendChild(canvas);
try {
const png = canvas.toDataURL('image/png');
console.log('Screenshot is readable:', png.slice(0, 30));
} catch (error) {
console.error('Canvas is still tainted:', error);
}
return canvas;
}
capture();
The documented imageTimeout default is 15,000 milliseconds; set it higher for slow, legitimate image responses or lower when a fast failure is preferable. Waiting for an img element to finish loading does not make a non-CORS response safe—it only prevents capturing before the request completes.
Verify that the fix worked
- The image response has
Access-Control-Allow-Originmatching the page origin. - Any preflight response allows the requested method and headers.
- The image’s final URL, including a redirect or CDN hostname, returns the header.
- html2canvas runs with
useCORS: true, and the image element usescrossorigin="anonymous"when you control it. canvas.toDataURL(),toBlob(), or a pixel read succeeds after capture.
For isolation, temporarily remove the S3 image from the target or add an ignore attribute:
Rank #3
- Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
- Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
- Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
- Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
- What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.
<img data-html2canvas-ignore="true"
src="https://bucket.s3.amazonaws.com/path/image.jpg"
alt="Optional image omitted from capture">
If the canvas works after that change, investigate the image response. If it still fails, inspect other images, CSS background URLs, SVG files, web fonts, iframes, and canvases nested in the target.
Use a same-origin proxy when S3 cannot change
When the image server cannot emit CORS headers, html2canvas documents a proxy as the fallback. The browser requests your own origin; your server fetches the permitted image and returns it with an image content type.
await html2canvas(document.querySelector('#capture'), {
proxy: '/image-proxy',
useCORS: true,
allowTaint: false
});
Example controlled proxy (Node.js 18+)
This example accepts only URLs from one S3 hostname. Adapt the allowlist to your application, authenticate the endpoint when appropriate, and enforce response-size and timeout limits in production.
import express from 'express';
const app = express();
const allowedHosts = new Set(['bucket.s3.amazonaws.com']);
app.get('/image-proxy', async (req, res) => {
const raw = typeof req.query.url === 'string' ? req.query.url : '';
let imageUrl;
try {
imageUrl = new URL(raw);
} catch {
return res.status(400).send('Invalid image URL');
}
if (imageUrl.protocol !== 'https:' || !allowedHosts.has(imageUrl.hostname)) {
return res.status(403).send('Image host is not allowed');
}
try {
const upstream = await fetch(imageUrl, { signal: AbortSignal.timeout(15000) });
if (!upstream.ok) return res.status(upstream.status).send('Upstream image failed');
const type = upstream.headers.get('content-type') || '';
if (!type.startsWith('image/')) return res.status(415).send('Not an image');
res.set('Content-Type', type);
res.set('Cache-Control', 'private, max-age=300');
const bytes = Buffer.from(await upstream.arrayBuffer());
return res.send(bytes);
} catch {
return res.status(502).send('Image proxy request failed');
}
});
app.listen(3000);
Do not turn this endpoint into an unrestricted open proxy. Validate the URL, restrict hosts or paths, require authentication if arbitrary users can call it, limit image size and duration, and consider rate limits. A proxy adds server work, latency, and another failure point, but it is the practical option when the source response cannot be changed.
Rank #4
- Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
- Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
- Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
- Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
- Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
Common errors and their fixes
| Symptom | Likely cause | Fix |
|---|---|---|
Image is visible, but toDataURL() throws a security error |
The response lacks a matching CORS header, or another resource tainted the canvas. | Inspect every final response; add the S3 rule and use useCORS: true, then isolate other resources. |
| Image is missing from the html2canvas output | The image failed to load, timed out, or was rejected as cross-origin. | Check the Network panel, wait for the image, verify the final URL’s headers, and adjust imageTimeout if the response is legitimately slow. |
useCORS: true changes nothing |
The option only requests a CORS load; S3 still does not authorize the page origin. | Correct the bucket rule, method, and requested headers. Confirm the response after redirects. |
| Preflight returns an error | The first matching S3 rule does not allow the requested method or one of the requested headers. | Update AllowedMethods and AllowedHeaders, then repeat the preflight. |
| It works in production but not locally | The localhost scheme, host, or port is a different origin and is not listed. | Add the exact development origin as a separate AllowedOrigins entry. |
| A proxy request is rejected | The proxy allowlist, URL validation, upstream status, or content-type check failed. | Log the server-side reason, permit only the intended S3 host/path, and return the upstream image content type. |
| A cross-origin iframe or existing canvas still fails | Browser isolation applies to nested documents and canvases as well. | Capture content served from your origin, configure CORS for every resource, or omit the element; html2canvas options cannot override the browser. |
Choose the right approach
| Approach | Use it when | Requirement | Trade-off |
|---|---|---|---|
S3 CORS plus useCORS |
You control the bucket and image responses. | A matching origin, method, and header rule, plus client-side CORS loading. | Requires precise configuration on every response path. |
| Same-origin proxy | You cannot change the image server. | A controlled server endpoint that fetches and returns the image. | Adds latency, server cost, maintenance, and security surface. |
| Exclude the image | The image is optional or decorative. | data-html2canvas-ignore or an ignore predicate. |
The screenshot intentionally loses that visual content. |
Performance and reliability considerations
- Capture only the required element instead of the entire document when possible; fewer images, fonts, and backgrounds mean fewer CORS checks and less work.
- Full-page captures can trigger lazy-loaded images. Ensure those images have loaded before capture and that their final responses carry the same CORS policy.
- Keep S3 and CDN cache behavior consistent. A cached response without the required header can appear to work intermittently when another response includes it.
- Signed URLs control object authorization, not canvas permission. The response still needs the appropriate CORS header.
- Use a higher timeout only for known slow resources. A long timeout can make a failed capture consume a browser request for much longer.
- Do not assume that a successful visual render proves the canvas is readable; always perform the export or pixel-read operation your application needs.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. It captures a URL on the server, so your page does not need html2canvas, an S3 CORS rule, or a browser proxy just to produce a screenshot. Before capture it accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
One GET request returns PNG, JPEG, WebP, or a PDF. The API supports full-page capture with lazy images loaded, a CSS-selector element, dark mode, 12 device presets or any viewport, retina scale, PDF paper size/margins/orientation/page ranges, HTML/CSS to image, custom JavaScript and CSS, clicks, selector/delay/network-idle waits, request or resource blocking, custom headers/cookies/user agents and Authorization, timezone and geolocation, transparent backgrounds, resizing, chosen-TTL caching, signed public-image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify a migration.
cURL
See the ScreenshotNeo documentation for the complete option list.
curl -G "https://api.screenshotneo.com/v1/shot"
-d access_key=YOUR_API_KEY
--data-urlencode url=https://app.example.com/page
-o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://app.example.com/page"},
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://app.example.com/page'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
Plans and billing
| Plan | Included shots | Price |
|---|---|---|
| Free | 1,000 per month | $0, no card |
| Starter | 3,000 | $5 |
| Growth | 15,000 | $15 |
| Pro | 60,000 | $39 |
| Scale | 250,000 | $99 |
| Business | 1,000,000 | $249 |
Every feature is available on every plan, and yearly billing gives two months free. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. If you want to stop maintaining browser setup, create a free ScreenshotNeo account: 1,000 screenshots a month are included with no card, and paid plans start at $5 for 3,000.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →FAQ
Can a signed S3 URL work with html2canvas?
Yes, provided the response reached through that signed URL includes a CORS header that authorizes the page origin. The signature controls whether S3 serves the object; it does not by itself make the response readable by canvas code.
Best Value
- 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
- Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
- Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
- HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
- What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
Is AllowedHeaders: ["*"] the same as allowing every website?
No. AllowedHeaders controls which request headers S3 accepts during CORS processing. The origins that may read the response are controlled separately by AllowedOrigins.
Why can a browser display the image when JavaScript cannot read it?
Displaying an image is permitted under broader browser rules than exposing its pixels to script. Canvas read operations require the image response to pass CORS, which is why a visually successful <img> load is not proof that html2canvas can export it.
Frequently Asked Questions
Can a signed S3 URL work with html2canvas?
Yes, provided the response reached through that signed URL includes a CORS header that authorizes the page origin. The signature controls whether S3 serves the object; it does not by itself make the response readable by canvas code.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Is AllowedHeaders: [“*”] the same as allowing every website?
No. AllowedHeaders controls which request headers S3 accepts during CORS processing. The origins that may read the response are controlled separately by AllowedOrigins.
Why can a browser display the image when JavaScript cannot read it?
Displaying an image is permitted under broader browser rules than exposing its pixels to script. Canvas read operations require the image response to pass CORS, so a visually successful img load is not proof that html2canvas can export it.
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.

