The Frustrating Red Console

You just migrated your static assets to a shiny new CDN. The site loads lightning fast, but your browser tab shows a generic gray globe. You open the DevTools console and see it bleeding red: Access to fetch at 'https://cdn.yoursite.com/site.webmanifest' from origin 'https://yoursite.com' has been blocked by CORS policy.

Welcome to the highly specific, incredibly annoying world of favicon CORS cross origin issues. If you are hosting your web manifests, SVG icons, or dynamically generated favicons on a different subdomain or third-party CDN, modern browsers will block them by default out of security concerns.

Let's fix this right now so you can get back to actual development.

The Quick Fix: HTML Attributes

If you are in a hurry, the problem is likely missing attributes in your HTML <link> tags. Browsers treat standard images and fetch requests differently. While an <img> tag usually bypasses strict CORS checks, fetching a JSON manifest or an SVG for dynamic manipulation does not.

To fix this immediately, add the crossorigin attribute to your manifest and icon links.

Update Your Web Manifest Link

<link rel='manifest' href='https://cdn.yoursite.com/site.webmanifest' crossorigin='use-credentials'>

Why use-credentials? If your web app manifest requires cookies or authorization to load (common in enterprise internal tools), you need this exact value. If your CDN is completely public, you can use crossorigin='anonymous' instead.

Update Your SVG Icon Link

<link rel='icon' type='image/svg+xml' href='https://cdn.yoursite.com/icon.svg' crossorigin='anonymous'>

If you are injecting icons via JavaScript or using them in a canvas element, this anonymous flag tells the browser to request the file without sending user credentials, which usually satisfies the browser's security model.

Root Causes: Why Do Browsers Block Favicons?

You might be wondering why a simple 16x16 pixel image triggers a security lockdown. It comes down to two specific modern web features.

1. The Fetch API and Web Manifests

Your site.webmanifest file is not an image; it is a JSON file. When the browser sees the rel='manifest' tag, it uses the Fetch API under the hood to download it. Any cross-origin Fetch request is subject to strict CORS policies. If your HTML is on www.example.com but the manifest is on assets.example.com, the browser blocks it unless the server explicitly permits it.

2. Tainted Canvas Security

If you are building dynamic favicons (like adding a red notification dot to your icon using JavaScript), you are likely drawing the icon to a <canvas> element, modifying it, and exporting it as a data URL. If the original image was loaded from a CDN without CORS headers, the browser marks the canvas as 'tainted'. Once a canvas is tainted, calling toDataURL() throws a fatal security error.

If you are struggling with basic visibility before even getting to CORS, check out our guide on troubleshooting missing favicons.

The Server-Side Fix: Configuring Your CDN

Fixing the HTML is only half the battle. If your CDN or storage bucket (like AWS S3 or Cloudflare) does not return the correct headers, the browser will still reject the files.

You must configure your asset server to return the Access-Control-Allow-Origin header. Here is how you do it across common setups.

Nginx Configuration

If you manage your own Nginx server for static assets, add this block to your configuration to allow cross-origin requests for manifests and images:

location ~* \.(webmanifest|svg|png|ico)$ {
    add_header Access-Control-Allow-Origin "*";
    add_header Access-Control-Allow-Methods "GET, OPTIONS";
}

AWS S3 CORS Configuration

If you are serving assets directly from an S3 bucket, you need to update the bucket's CORS policy via the AWS console using this JSON snippet:

[
    {
        "AllowedHeaders": ["*"],
        "AllowedMethods": ["GET", "HEAD"],
        "AllowedOrigins": ["https://www.yoursite.com"],
        "ExposeHeaders": []
    }
]

Real-World Example: How GitHub Handles It

Look at GitHub. They host user avatars and UI icons on avatars.githubusercontent.com. If you inspect their network requests, you will see they explicitly send Access-Control-Allow-Origin: * for all these assets.

Why? Because GitHub relies heavily on dynamic UI updates. If they didn't configure their CORS headers perfectly, any client-side script attempting to read pixel data from those images would crash the browser tab's JavaScript execution. They pair this server header with the crossorigin='anonymous' attribute in their HTML, creating a bulletproof setup.

Prevention Tips and My Personal Opinion

After debugging these issues for years, I have a strong opinion on this: Keep your primary favicon.ico on your root domain.

For modern assets like the web manifest and high-res SVGs, hosting them on a CDN is fine, provided you configure the headers as shown above.

Conclusion

Favicon CORS cross origin issues are a headache, but they are entirely predictable once you understand the browser's security model. Add the crossorigin attribute to your link tags, ensure your CDN sends the Access-Control-Allow-Origin header, and keep a fallback icon on your root domain.

If you want to bypass CDN issues entirely for small projects, use Mzu favicondl to generate a perfectly optimized set of local icons. Sometimes, the best way to fix cross-origin issues is to stay on the same origin.