Hosting Web fonts on a CDN? You’re going to need some CORS.

Old Coors can

Yeah, you wish this was about beer.

So you’re using some trick Web fonts for your site or app. It looks awesome in development. Yay.

You push to production, where your assets are all hosted on a CDN and suddenly your fonts stop working on Firefox and probably Internet Explorer (9+), too. You see crappy little squares where awesome fonts should be.

Bang. You’ve just run into the browsers’ same-origin policy restrictions.

I experienced this problem on Schock.net, where my assets are hosted on Amazon S3 with Cloudfront as the CDN. I installed the Font Awesome pack, which is loaded like so with the CSS3 @font-face rule:

@font-face {
    font-family: 'FontAwesome';
    src: url('../font/fontawesome-webfont.eot?v=3.2.0');
    src: url('../font/fontawesome-webfont.eot?#iefix&v=3.2.0')
           format('embedded-opentype'),
         url('../font/fontawesome-webfont.woff?v=3.2.0') format('woff'),
         url('../font/fontawesome-webfont.ttf?v=3.2.0') format('truetype'),
         url('../font/fontawesome-webfont.svg#fontawesomeregular?v=3.2.0')
           format('svg');
}

@font-face causes requests to http://static.schock.net/font. So with the origin being at http://schock.net and the CDN being a different domain, the browser is all like, NUH-UH! Fonts denied!

Just tell me how to fix it already

Assuming you’re using S3, the fix is in your CORS configuration in your S3 bucket configuration:

  1. Go to your AWS console.
  2. Choose your bucket.
  3. Choose Properties.
  4. Choose Edit CORS Configuration.

Here is the basic XML (via StackOverflow) for allowing your fonts to be loaded from a specified origin. You’ll need to modify it to work with your particular setup.

<CORSConfiguration>
    <CORSRule>
        <AllowedOrigin>http://mydomain.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Content-*</AllowedHeader>
        <AllowedHeader>Host</AllowedHeader>
    </CORSRule>
    <CORSRule>
        <AllowedOrigin>http://*.mydomain.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Content-*</AllowedHeader>
        <AllowedHeader>Host</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Be patient. I monkeyed around with my config and it worked at first, then later it didn’t. There are some caching issues going on here, so if you get it wrong and want to update the CORS config, you will probably want to invalidate your font files via the AWS Cloudfront interface.

Update 10/7/2014: Cloudfront cache invalidation only seems to be necessary if you are updating font file versions. Instead, there seems to be perhaps 5-10 minutes of delay from when you update the bucket policy XML to when it actually gets propagated to Cloudfront. Be patient.

Some people suggest the fix is <AllowedOrigin>*</AllowedOrigin>. Sure, this will probably work, but you’ve also just allowed every asset in your bucket to be accessed by any domain without any cross-origin restrictions. Problem? Maybe.

And what’s up with the AllowedHeader rules, you’re wondering? These are for CORS preflight requests, and if a preflight request asks for these headers, then this says that these are the ones that are ok to send with the actual request. Why they’re necessary to make your Web fonts work isn’t entirely clear, but it seems like it might get around a Firefox bug, according to the StackOverflow article. (Yes, CORS preflight is confusing.)

So why CORS?

Because same-origin policy. CORS is a way by which to relax this policy; it’s an alternative to JSONP minus some of the security concerns that script injection is subject to.

But why are *fonts* restricted by same-origin policy in Firefox and IE?

Yes, what a fantastic question. The simple answer is that the same-origin restriction for fonts is in the CSS3 Fonts working spec, and Firefox and IE are adhering strictly to this.

But fonts aren’t scripts. They can’t be executed to do malicious things. So the deeper question is, what’s the motivation behind this?

Same-origin policy was a security feature, and now it seems like the purpose is being inflated onto the slippery slope of intellectual property control. That is to say, font foundries, who command wheelbarrows full of money to license their fonts, don’t want it to be easy for freeloaders to illegally use their fonts with a simple @font-face rule pointing to a legally licensed domain.

This seems like a whole separate W3 standards discussion altogether, no? So my guess is that the Google Chrome folks don’t agree with this interpretation, and that’s why Chrome doesn’t apply same-origin policy to fonts as of yet.

Of course, I’m speculating somewhat, so take it with a grain of salt can of Coors.


Update 12/2014

A Google engineer wrote a whole book on the subject of CORS! I haven’t read it, but probably worth a look if you need something in-depth.


More CORS reading

Books on Amazon Web Services (AWS)

  • keith-sparkjoy

    I think there’s a risk serving fonts off CloudFront using this technique: CloudFront caches the CORS header response from S3, which is specific to the host that requested it. Say you have a font that has fallen out of cache in CloudFront and someone requests it from evil.com – CloudFront will remember “evil.com” as the cross origin requestor because it’s caching those response headers from S3. So next time your pages from, say, foo.com request the font, the browser will send Origin: foo.com but receive the cached Key Access-Control-Allow-Origin: http://evil.com and IE / FireFox will fail to render the font on your site.

    What kills me is that CORS headers are *not* used for Javascript files, which is really where danger lies, but they are being used for fonts. So dysfunctional…

    Here’s a suggested workaround: https://forums.aws.amazon.com/message.jspa?messageID=422504#422532

  • Bort

    Thank you, this was driving me insane.

  • Ray

    Cloud front now allows you to ‘whitelist’ headers to pass through to origin and for determining if it need to cache 2 different resources because of different origins. I just set it up to pull from an s3 bucket from my site that supports both http and https as well as several sub domains. The setup is in the behavior view for a cloud front distribution.

  • Adrian Holovaty

    If you use CloudFront, you’ll need to also tweak CloudFront to forward the “Origin” header. See my blog post here: http://www.holovaty.com/writing/cors-ie-cloudfront/

  • Steve

    This worked *perfectly* thank you Sir.

  • gigajack

    Thanks for the article! I’m still using S3 but I moved away from CloudFront. I’m now using http://www.keycdn.com for content delivery. They also offer CORS support.

  • Shivam

    I want to use fonts for different domain like

    http://abc.com

    http://xyz.com

    and

    http://*.abc.com

    http://*.xyz.com

    but abc.com is not taking up fonts now.

  • predman

    Thanks buddy for the tip, just ran into this issue recently and this helped!

  • Mary Smith

    Thanks for the useful information. I’m using S3 for several sites and I’m quite satisfied, also working with http://cdnsun.com/ for some delivery networks which have connection with lots of media.

  • Mary Smith

    Thanks for the useful information. I’m using S3 for several sites and I’m quite satisfied, also working with http://cdnsun.com/ for some delivery networks which have connection with lots of media.

  • Roshan

    *

    GET

    PUT

    POST

    DELETE

    3000

    *

    Authorization

    But no luck. Web fonts are not loaded. please help me.

  • Really nice reading. I have not used yet S3 but it’s good to know how fix cross origin restriction on all platforms. I’ve just wrote a blog post on how to use CORS to fix cross-domain issue with @font-face. See http://zinoui.com/blog/cross-domain-fonts