DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
Skip to content
AWS S3

How to Fix html2canvas CORS Errors with AWS S3 Across Browsers

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

If html2canvas omits S3 images or throws Failed to execute 'toDataURL', the usual cause is a canvas tainted by an image response that did not authorize your page’s origin. Set useCORS: true, then configure CORS on the endpoint the browser actually calls (S3 or CloudFront). Keep allowTaint disabled when you need to export pixels. If the endpoint cannot be changed, route images through a same-origin proxy.

What the error means

html2canvas rebuilds a visual representation from the DOM; it does not capture the browser’s composited screen and cannot bypass browser security rules. When an image from another origin is drawn without a successful CORS response, the browser marks the canvas as tainted. Pixel reads and export methods such as toDataURL(), toBlob(), and getImageData() are then blocked.

With its documented defaults, useCORS is false and allowTaint is false. The latter causes html2canvas to skip an image that would taint the canvas. Setting allowTaint: true does not make the pixels exportable; it allows a tainted canvas, which is the opposite of the requirement when you need a PNG, JPEG, or pixel data.

A public S3 object can therefore display in an <img> while remaining unusable in a canvas. Object authorization and CORS authorization are separate controls.

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

1. Find the real image endpoint first

  1. Open the page containing the capture target.
  2. In browser developer tools, select Network, filter by Img, and reload.
  3. Open the image request that fails or is skipped. Record the final URL after redirects, status code, and response headers.

Determine whether the browser receives the file from an S3 REST endpoint, an S3 website endpoint, or a CloudFront distribution. Test the exact browser-facing URL, not merely the bucket URL you expect to be used. Check the response’s Access-Control-Allow-Origin. For a credential-free image request, it must authorize the page origin (for example, https://app.example.com), or use an appropriate wildcard for genuinely public, non-credentialed use.

Also inspect the request’s Origin, method, and any requested headers. A redirect can move the request to a different host where CORS headers are absent.

2. Tell html2canvas to request the image with CORS

Enable the library option on the capture call:

import html2canvas from 'html2canvas';

const element = document.querySelector('#report');
const canvas = await html2canvas(element, {
  useCORS: true,
});

const png = canvas.toDataURL('image/png');

useCORS only tells html2canvas to attempt a CORS image load. It cannot add Access-Control-Allow-Origin to an S3 response. Every image included in the DOM, including images inside nested elements, must be served with a response that the browser accepts.

Use the project’s documented configuration and limitations pages for the complete option list: configuration, getting started, and limitations.

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

3. Configure the S3 bucket CORS rule

In the S3 console, open the bucket, choose Permissions, then Cross-origin resource sharing (CORS), and save a JSON rule that matches the page origin and image method. A least-privilege example is:

[{
  "AllowedOrigins": ["https://app.example.com"],
  "AllowedMethods": ["GET"],
  "AllowedHeaders": []
}]

Replace the origin with the exact scheme, host, and port shown in the page’s Origin request header. http://localhost:3000 and https://app.example.com are different origins. Add only request headers your application actually sends. If the browser performs a preflight, those headers must be allowed; do not add a broad wildcard simply because an example used one.

For a public, credential-free image service, "AllowedOrigins": ["*"] can be valid. A specific origin is easier to audit and prevents a broad policy from being mistaken for a requirement. CORS does not grant read permission: the object still needs an applicable bucket policy, ACL, or other S3 authorization.

S3 evaluates the first CORS rule matching the request’s origin, method, and requested headers. Rule order therefore matters. AWS documents the matching behavior and the testing procedure in its CORS overview and CORS testing guide.

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

Verify a preflight when one occurs

Most simple image GETs do not require preflight, but custom headers, credentials, or non-simple methods can trigger an OPTIONS request. Reproduce it from a shell:

curl -i -X OPTIONS 'https://bucket.s3.amazonaws.com/images/logo.png' 
  -H 'Origin: https://app.example.com' 
  -H 'Access-Control-Request-Method: GET' 
  -H 'Access-Control-Request-Headers: authorization'

The response must match the requested origin, method, and headers. If the rule does not match, S3 returns no usable CORS headers; editing JavaScript cannot fix that response.

4. Handle CloudFront correctly

If the page uses a CloudFront URL, configure and test CloudFront, even when direct S3 access works. The distribution must forward Origin to the S3 origin so S3 can generate the correct response. For cached preflight responses, forward Origin, Access-Control-Request-Method, and Access-Control-Request-Headers, and make the cache behavior vary on those inputs. Otherwise CloudFront can cache a response without the needed CORS header and reuse it for another origin.

Compare headers at both endpoints:

curl -I 'https://bucket.s3.amazonaws.com/images/logo.png' 
  -H 'Origin: https://app.example.com'

curl -I 'https://cdn.example.com/images/logo.png' 
  -H 'Origin: https://app.example.com'

If S3 includes Access-Control-Allow-Origin but CloudFront does not, investigate origin forwarding, the distribution’s cache policy, response-header policy, and stale cached objects. AWS’s CloudFront origin and CORS guidance and S3 CORS troubleshooting cover these interactions.

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

5. Retest and isolate the remaining asset

  1. Hard-refresh the page or clear the relevant CDN cache after changing headers.
  2. Capture a minimal element containing one known-good S3 image.
  3. Confirm the image response contains the expected CORS header and that html2canvas is called with useCORS: true.
  4. Add other images, CSS backgrounds, SVGs, and canvases one at a time.
  5. Only after the capture succeeds, call toDataURL, toBlob, or pixel-reading APIs.

A single unapproved image or a pre-existing tainted canvas anywhere in the captured subtree can taint the final canvas. Browser behavior is enforced by the security model, not by html2canvas. The project lists current Chrome/Chromium, Firefox, and Safari as supported evergreen browsers; still compare the actual network response in every browser you target.

Common symptoms and precise fixes

S3 image is missing from the capture

  • Confirm the URL is reachable and returns an image, not an access-denied page or redirect.
  • Check that the final response has an origin-authorizing header.
  • Enable useCORS: true. With the default allowTaint: false, html2canvas may skip an image that would taint the canvas.
  • If you cannot configure the image host, use a same-origin proxy that fetches the asset server-side and serves it from your application origin.

toDataURL or toBlob throws a security error

Find every cross-origin image and canvas in the captured subtree. One asset loaded without CORS approval is enough to taint the result. Fix that response or remove the asset before capture; changing allowTaint will not restore export access.

The object returns 403 or 404

Resolve S3 authorization, object key, region, and redirect issues separately from CORS. A CORS rule does not make a private object public.

Direct S3 works but CloudFront fails

Inspect the distribution response, not just the origin. Forward the CORS request headers, vary cached responses appropriately, and invalidate or wait out an object whose response was cached without CORS headers.

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.

The rule looks correct but no CORS header appears

Compare the exact Origin, method, and requested headers with the rule; check that an earlier rule is not matching first; and test the endpoint with an OPTIONS request when preflight is present.

It works in one browser but not another

Capture the network trace in each browser and compare redirects, final URLs, request mode, and response headers. Do not assume a library flag can bypass browser enforcement. Also check for browser extensions, service workers, or cached CDN responses that differ between profiles.

S3 CORS versus a same-origin proxy

Approach Best when What you control Costs and risks
S3/CloudFront CORS You own the asset delivery configuration Origin, methods, headers, and CDN cache variation Requires correct deployment and cache configuration; object authorization remains separate
Same-origin proxy The asset host cannot return the required headers Your server’s response headers and fetch policy Adds server bandwidth, latency, validation, and SSRF/access-control responsibilities

Neither method is universally faster or safer. Choose the control point you can operate reliably, restrict proxy destinations if you use one, and avoid turning a private-image proxy into an unrestricted URL fetcher.

Performance and reliability notes

  • Start with a small element and one image to separate CORS from layout, font, and memory problems.
  • Large full-page DOMs and high-resolution images increase canvas memory use; capture only the required region when possible.
  • CloudFront caching can improve repeat loads, but cached CORS responses must vary on the relevant request headers.
  • After changing S3 or CloudFront configuration, verify the response observed by the browser rather than relying on console settings alone.
  • Record the final URL, status, and CORS headers in bug reports; these facts usually identify the failing layer quickly.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is a dependable website image rather than a DOM reconstruction, ScreenshotNeo provides a single HTTP request. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, 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. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

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

Use the API documentation at screenshotneo.com/docs/. The following call captures a clean WebP image:

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

Equivalent 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)

Equivalent 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}`);
const file = Buffer.from(await res.arrayBuffer());

Every plan includes the available features, including full-page and element capture, device presets, retina scale, PDF controls, custom CSS and JavaScript, selector waits, request blocking, headers and cookies, timezone and geolocation, resizing, TTL caching, signed links, asynchronous webhooks, bulk capture, usage information, and an OpenAPI specification. Pricing is Free for 1,000 shots per month with no card, then Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000; annual billing provides two months free. Create a free ScreenshotNeo account to start with 1,000 screenshots a month and no card.

Reference documentation

Frequently Asked Questions

Does adding crossorigin="anonymous" alone fix an S3 image?

No. It requests a CORS-enabled load, but S3 or CloudFront must still return an Access-Control-Allow-Origin value that matches the page origin.

Can I use credentials with a wildcard origin?

Credentialed cross-origin requests require an explicit authorized origin; a wildcard is not a substitute. Configure credentials and allowed headers only when your application actually needs them.

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

Why does a normal browser tab display the image when html2canvas cannot export it?

Displaying an image does not grant script access to its pixels. Canvas export is allowed only after the image response passes the browser’s CORS checks.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.