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

The usual fix is a three-part chain: configure Cloud Storage CORS for the exact web origin and GET, set crossOrigin = "anonymous" before assigning the Firebase download URL, and call html2canvas with useCORS: true and allowTaint: false. A Firebase image can display normally in an <img> while still being unusable when the browser tries to export a canvas or PDF.

Why a Firebase image loads but fails in a canvas or PDF

Firebase Storage’s getDownloadURL(ref(storage, path)) returns a URL on a storage origin, not necessarily the origin serving your application. For example, your app may run at https://app.example.com while the image is served from firebasestorage.googleapis.com. That makes the request cross-origin.

Browsers allow many cross-origin images to be painted on screen. Canvas export is stricter: unless the image response is CORS-readable, drawing it taints the canvas. A tainted canvas cannot be read with toDataURL() or getImageData(), so jsPDF receives no usable image. Firebase’s browser download guidance explicitly says that direct browser downloads require configuring the Cloud Storage bucket for cross-origin access.

Changing jsPDF settings cannot repair a response that lacks Access-Control-Allow-Origin. Check the HTTP response first.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Elebase USB to USB C Adapter for iPhone 18 Pro Max,USBC Car Charger Adapter
  • 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.

Diagnose the failure before changing code

  1. Generate the actual download URL

    Use the Firebase SDK and keep the URL out of production logs because download URLs can grant access to the object.

    import { getStorage, ref, getDownloadURL } from "firebase/storage";
    
    const storage = getStorage();
    const url = await getDownloadURL(ref(storage, "images/photo.jpg"));
    console.log(url); // Use temporarily while debugging
  2. Inspect the response from the deployed origin

    Open DevTools, choose Network, reload the page, and select the image request. Check the status, Content-Type, and Access-Control-Allow-Origin. The allowed origin must match the page origin (scheme, host, and port), such as https://app.example.com. https://www.example.com, http://localhost:5173, and http://localhost:3000 are different origins.

  3. Separate access rules from CORS

    A 401 or 403 means Firebase Storage Rules or authentication rejected the object. A 404 means the path or object is wrong. Those are not canvas-taint errors. Resolve object access first, then inspect CORS headers on a successful response.

  4. Check the media type

    Ensure the response has an image content type such as image/jpeg or image/png. An HTML error page, redirect, or login response saved as an image will fail even when CORS is correct.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
    Rank #2
    Anker USB-C Hub, 5-in-1 USB Hub for Laptops, 4K HDMI Multiport Adapter
    • 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.

Configure Cloud Storage CORS correctly

CORS tells Cloud Storage which browser origins may read a response. Add every real development and production origin, and allow GET. Do not use a broad wildcard when credentials or private data are involved.

[
  {
    "origin": [
      "http://localhost:5173",
      "http://localhost:3000",
      "https://app.example.com"
    ],
    "method": ["GET"],
    "responseHeader": ["Content-Type"],
    "maxAgeSeconds": 3600
  }
]

Save this as a CORS policy file and apply it to the bucket with the Google Cloud Storage tooling documented for your project. Use the bucket that actually serves the Firebase Storage object. After deployment, make a fresh request from the deployed site and verify that the response contains Access-Control-Allow-Origin: https://app.example.com (or the matching development origin).

CORS policy changes can be cached. Test in a new private window or wait for the configured cache lifetime before concluding that a change failed. A policy cannot grant access to an origin that is not listed, and it does not replace Firebase Storage Rules.

Load the image with CORS enabled

Set crossOrigin before src. Setting it afterward is too late because the browser may already have started the request. Wait for decoding before asking html2canvas to render the element.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
Sale
Anker USB C Hub, 7in1 Multi-Port USB Adapter, 4K@60Hz USBC to HDMI Splitter
  • 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.
import { getStorage, ref, getDownloadURL } from "firebase/storage";
import html2canvas from "html2canvas";
import { jsPDF } from "jspdf";

const storage = getStorage();
const url = await getDownloadURL(ref(storage, "images/photo.jpg"));

const img = new Image();
img.crossOrigin = "anonymous"; // Must precede src
img.src = url;
await img.decode();
document.querySelector("#preview").replaceChildren(img);

const canvas = await html2canvas(document.querySelector("#invoice"), {
  useCORS: true,
  allowTaint: false
});

const pdf = new jsPDF();
pdf.addImage(canvas.toDataURL("image/png"), "PNG", 10, 10, 190, 0);
pdf.save("invoice.pdf");

html2canvas has useCORS: false by default, so opt in explicitly. Keep allowTaint: false; setting it to true may let rendering proceed but prevents reading the resulting canvas, which defeats PDF export. Its default image timeout is 15,000 ms, so slow images can also abort a capture.

A complete reusable capture function

This version reports useful failures and waits for images already present in the document.

import html2canvas from "html2canvas";
import { jsPDF } from "jspdf";

function waitForImage(image) {
  if (image.complete && image.naturalWidth > 0) {
    return image.decode ? image.decode().catch(() => {}) : Promise.resolve();
  }
  return new Promise((resolve, reject) => {
    image.addEventListener("load", () => resolve(), { once: true });
    image.addEventListener("error", () => reject(new Error(`Image failed: ${image.src}`)), { once: true });
  });
}

export async function makePdf() {
  const images = [...document.querySelectorAll("#invoice img")];
  for (const image of images) {
    image.crossOrigin = "anonymous";
    await waitForImage(image);
  }

  const canvas = await html2canvas(document.querySelector("#invoice"), {
    useCORS: true,
    allowTaint: false,
    imageTimeout: 15000
  });

  const dataUrl = canvas.toDataURL("image/png");
  const pdf = new jsPDF({ unit: "mm", format: "a4" });
  const pageWidth = pdf.internal.pageSize.getWidth();
  const margin = 10;
  const width = pageWidth - margin * 2;
  const height = canvas.height * width / canvas.width;
  pdf.addImage(dataUrl, "PNG", margin, margin, width, height);
  pdf.save("invoice.pdf");
}

If you create images dynamically, apply crossOrigin before setting each src; changing an existing element’s property requires reloading its source.

When direct bucket CORS is not the right design

Approach Storage-policy control URL exposure Authentication Operational complexity Where rendering occurs
Direct bucket CORS You manage the bucket policy and exact origins Firebase download URL reaches the browser Firebase Rules and any download-token requirements still apply Lowest once configured Browser
Same-origin proxy Your server returns a controlled CORS response Original storage URL can stay server-side Proxy can enforce application authentication Moderate: fetch, stream, cache, and secure an endpoint Browser after proxy fetch
Server-side PDF rendering Server accesses storage directly Image need not be exposed to the browser Server credentials and authorization are required Highest: browser automation or a PDF renderer Server

Use a controlled same-origin proxy when the bucket policy cannot be changed or when exposing download URLs is undesirable. The proxy must fetch the image, return the correct Content-Type, and emit a CORS policy for your application. html2canvas supports a proxy option for cross-origin loading when direct access is unavailable. Do not rely on browser extensions that disable CORS; they alter only one local browser and hide the production problem.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
UGREEN USB to USB C Adapter Combo 4-Pack, 10Gbps USB C Converter Space Gray
  • 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
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common errors and precise fixes

“Tainted canvases may not be exported”

The image was drawn without a successful CORS response, or another asset in the captured element tainted the canvas. Verify every image, background image, font-related asset, and iframe involved in the capture. Confirm the response header, then reload the image with crossOrigin set first.

The image is visible but missing from the PDF

Check that the element is in the DOM and fully loaded when capture starts. Await img.decode(), avoid lazy-loading that has not been triggered, and inspect the Network panel for failed requests. Set useCORS: true.

“Access-Control-Allow-Origin” is missing

The bucket policy was not applied to the serving bucket, the deployed origin is absent, or a cached response is being reused. Compare the exact origin in the request with the policy and test again after cache expiry.

401, 403, or 404 responses

Fix the Firebase path, authentication state, or Storage Rules first. CORS headers cannot authorize a user who is denied by Storage Rules.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Anker USB C Hub, 5-in-1 USBC to HDMI Splitter with 4K Display
  • 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.

Works on localhost but not production

Add the production scheme and hostname to the policy. Include the port for local development, and remember that HTTP and HTTPS are distinct origins.

Timeout after 15 seconds

html2canvas’s documented default image timeout is 15,000 ms. Improve image delivery, trigger lazy images earlier, or set an appropriate imageTimeout for your workload. A longer timeout does not fix a denied request.

Performance and reliability practices

  • Capture only the required container instead of the entire document.
  • Resize very large source images before embedding them; canvas memory use grows with pixel dimensions.
  • Use JPEG output for photographic content when a smaller PDF is acceptable; use PNG for text, line art, and transparency.
  • Wait for web fonts and asynchronous data before capture to prevent layout shifts.
  • Keep allowTaint: false so failures are detected instead of producing an unreadable export.
  • Test from the exact deployed origin, not only a development server.

Or skip the browser setup

For a URL-to-image or PDF job, ScreenshotNeo makes the request server-side. Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and each response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

See the ScreenshotNeo API documentation for all options. A direct call looks like this:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Replace the example URL with the public page you need to capture. ScreenshotNeo supports PNG, JPEG, WebP, and PDF output plus custom CSS and JavaScript, selectors, device presets, wait conditions, headers, cookies, and asynchronous jobs. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Verification checklist

  • The Firebase URL is generated with getDownloadURL and points to the expected object.
  • The response is successful and has an image Content-Type.
  • The bucket CORS policy lists the exact deployed origin and GET.
  • crossOrigin = "anonymous" is set before src.
  • All images are loaded before capture.
  • html2canvas uses useCORS: true and allowTaint: false.
  • The canvas can be read with toDataURL() before jsPDF is called.

Frequently Asked Questions

Can jsPDF load a Firebase Storage URL directly?

jsPDF receives image data; first make the URL CORS-readable, render it into a clean canvas, and then pass the canvas data URL to jsPDF.

Should I set html2canvas allowTaint to true?

No for PDF export. A tainted canvas cannot be read, so keep allowTaint false and correct the server CORS response.

Do Firebase Storage Rules configure CORS?

No. Storage Rules decide whether the object may be read; the bucket CORS policy decides whether a browser page may read the cross-origin response.

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

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.