Tainted Canvas: why the browser blocks your canvas (and how to unblock it)

Working with the feels simple at first, load an image, draw it, export it. But the moment that image lives on a different origin you hit an error: calls such as canvas.toDataURL() or getImageData() shows error with “Tainted canvases may not be exported.” The browser is preventing you from reading the pixels of that canvas. Tainted canvas error What’s with the tainted canvas? When you draw an image fetched from another origin without proper CORS setup, the browser makes the entire canvas as “tainted.” Any attempt to read those pixels (getImageData, toBlob, toDataURL, etc.) is then blocked. To solve this, you need to add crossOrigin="anonymous" to asks the browser to send the request in CORS mode, but the server must still reply with a matching Access-Control-Allow-Origin header. Solving the tainted canvas error You’ve got two options to solve the tainted canvas error: Control the server? Add the header Access-Control-Allow-Origin: * (or your domain) and keep crossOrigin="anonymous" on your tag or drawImage source. Don’t control the server? Backend relay: Fetch the image in your own backend, then serve it with the right CORS header so the browser sees it as same-origin. CORS proxy: Use a CORS proxy that injects the header for you, ideal if you are running a static only site or don't have a backend. const image = new Image(); image.crossOrigin = "anonymous"; image.src = "https://proxy.corsfix.com/?https://example.com/image.png"; image.onload = () => { const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); ctx.drawImage(image, 0, 0); const dataUrl = canvas.toDataURL(); console.log(dataUrl); }; Conclusion A tainted canvas is just the browser’s way of preventing you from accessing a canvas that contains cross-origin data. Tell the browser to expect CORS (crossOrigin="anonymous") and tell the server to allow it (Access-Control-Allow-Origin). If you can’t modify the server, relay the image through your backend or use a CORS proxy. Need something production-ready? Give Corsfix a spin, it’s free to start and only costs when you scale.

Apr 30, 2025 - 11:29
 0
Tainted Canvas: why the browser blocks your canvas (and how to unblock it)

Working with the feels simple at first, load an image, draw it, export it. But the moment that image lives on a different origin you hit an error: calls such as canvas.toDataURL() or getImageData() shows error with “Tainted canvases may not be exported.” The browser is preventing you from reading the pixels of that canvas.

Tainted canvas error
Tainted canvas error

What’s with the tainted canvas?

When you draw an image fetched from another origin without proper CORS setup, the browser makes the entire canvas as “tainted.” Any attempt to read those pixels (getImageData, toBlob, toDataURL, etc.) is then blocked.

To solve this, you need to add crossOrigin="anonymous" to asks the browser to send the request in CORS mode, but the server must still reply with a matching Access-Control-Allow-Origin header.

 src="https://example.com/image.png" crossorigin="anonymous" />

Solving the tainted canvas error

You’ve got two options to solve the tainted canvas error:

  • Control the server?

    Add the header Access-Control-Allow-Origin: * (or your domain) and keep crossOrigin="anonymous" on your tag or drawImage source.

  • Don’t control the server?

    • Backend relay: Fetch the image in your own backend, then serve it with the right CORS header so the browser sees it as same-origin.
    • CORS proxy: Use a CORS proxy that injects the header for you, ideal if you are running a static only site or don't have a backend.
  const image = new Image();
  image.crossOrigin = "anonymous";
  image.src = "https://proxy.corsfix.com/?https://example.com/image.png";
  image.onload = () => {
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");
    ctx.drawImage(image, 0, 0);
    const dataUrl = canvas.toDataURL();
    console.log(dataUrl);
  };

Conclusion

A tainted canvas is just the browser’s way of preventing you from accessing a canvas that contains cross-origin data. Tell the browser to expect CORS (crossOrigin="anonymous") and tell the server to allow it (Access-Control-Allow-Origin). If you can’t modify the server, relay the image through your backend or use a CORS proxy. Need something production-ready? Give Corsfix a spin, it’s free to start and only costs when you scale.