# Serving Fonts from CDN

By  on

For maximum performance, we all know we must put our assets on CDN (another domain).  Along with those assets are custom web fonts.  Unfortunately custom web fonts via CDN (or any cross-domain font request) don't work in Firefox or Internet Explorer (correctly so, by spec) though they do work (incorrectly so) in Webkit-based browsers. They actually require custom CORS configurations to display properly.  Here's the code you'll need to make that happen.

## The .htaccess or httpd.conf Code

The code can be placed with the .htaccess file or httpd.conf file:

# Apache config
<FilesMatch ".(eot|ttf|otf|woff)">
</FilesMatch>

# nginx config
if ($filename ~* ^.*?\.(eot)|(ttf)|(woff)$){
}


This sets the Access-Control-Allow-Origin CORS configuration to allow pulling from all domains.  List specific domains by comma if you want to serve fonts up to only specific domains.  You'll want to serve up all font types appropriately in the case that the browser prefers one type.

To ensure the header is set properly, you can check using the curl utility:

$curl -I https://some.cdn.otherdomain.net/media/fonts/somefont.ttf # Result HTTP/1.1 200 OK Server: Apache X-Backend-Server: developer1.webapp.scl3.mozilla.com Content-Type: text/plain; charset=UTF-8 Access-Control-Allow-Origin: * ETag: "4dece1737ba40" Last-Modified: Mon, 10 Jun 2013 15:04:01 GMT X-Cache-Info: caching Cache-Control: max-age=604795 Expires: Wed, 19 Jun 2013 16:22:58 GMT Date: Wed, 12 Jun 2013 16:23:03 GMT Connection: keep-alive  If you see Access-Control-Allow-Origin: * in the response, you're golden! This same strategy is used by Bootstrap CDN, so you know it's good! ## Recent Features • By ### Create a Sheen Logo Effect with CSS I was inspired when I first saw Addy Osmani's original ShineTime blog post. The hover sheen effect is simple but awesome. When I started my blog redesign, I really wanted to use a sheen effect with my logo. Using two HTML elements and... • By ### Designing for Simplicity Before we get started, it's worth me spending a brief moment introducing myself to you. My name is Mark (or @integralist if Twitter happens to be your communication tool of choice) and I currently work for BBC News in London England as a principal engineer/tech... ## Incredible Demos • By ### Animated Progress Bars Using MooTools: dwProgressBar I love progress bars. It's important that I know roughly what percentage of a task is complete. I've created a highly customizable MooTools progress bar class that animates to the desired percentage. The Moo-Generated XHTML This DIV structure is extremely simple and can be controlled... • By ### MooTools Text Flipping There are lots and lots of useless but fun JavaScript techniques out there. This is another one of them. One popular April Fools joke I quickly got tired of was websites transforming their text upside down. I found a jQuery Plugin by Paul... ## Discussion 1. Great tip, thanks. You might clarify however that it’s not so much that Firefox and Internet Explorer “don’t work”, as it is the fact that Chrome and others simply have incorrect defaults when handing CORs. At least that’s my understanding. 2. Another option is to set your CORS configuration in S3 if you’re planning on using Amazon. 3. Related: Serve from AWS S3/CloudFront – https://coderwall.com/p/ub8zug 4. Or for those of use on nginx: if ($filename ~* ^.*?\.(eot)|(ttf)|(woff)$){ add_header Access-Control-Allow-Origin *; }  5. Crispen Smith I think it’s important to state that this is an Apache specific approach. We are seeing more and more services such as Node being used as back-end. I see others have already spoken to *nix and S3 solutions but I think is likely a solution available within a pure Node as well. However, for Node developers I suspect the solution is much less universal and dependant on what’s being used. Connect’s static middleware can probably be overridden to write additional headers for those using anything that includes connect. 6. Rolf Better allow only certain sites/domain instead of * if it is a custom or licensed font (in case of piracy). 7. Andrew Killinger Any chance the @font-face syntax mentioned by Paul Irish would solve the problem? http://www.paulirish.com/2009/bulletproof-font-face-implementation-syntax/ • No, @font-face would work for fonts hosted on your own server, but not remotely hosted fonts with Amazon S3. Amazon does not send an appropriate CORS header response with the fonts. 8. Thanks for this, you saved me. I found the answer everywhere for the amazon hosting solution, but everyone had failed to mention you can just ad this to the .htaccess file if your hosting somewhere else. 9. Hello (sorry don’t speak english verry well) I use on my website CDN (cdn4.kanucomputer.net,cdn5.kanucomputer.net,cdn6.kanucomputer.net) all to my root folder www. All work with chrome but with IE et FF no icons. My host send my this article to fix the problem, but when I add the code upside in my .htaccess I encounter a “500 internal server error” ??? Someone could help me ? Thanks a lot. Have a good day 10. Same problem: Additionally, a 500 Internal Server Error error was encountered while trying to use an ErrorDocument to handle the request. Total htaccess file: php_value magic_quotes_gpc 0 Header set Access-Control-Allow-Origin "*" if ($filename ~* ^.*?\.(eot)|(ttf)|(woff)$){ add_header Access-Control-Allow-Origin *; }  11. Kevin (and other), i fixed it using this code: Header set Access-Control-Allow-Origin "*" and paste it at the top of your .htaccess file And it should work. However, the nginx config fix, should be modified like this aswell. How that works, I would not know. Cheers 12. justin So to be clear, does implementing this .htaccess solution make externally controlled fonts work in IE and FF? Specifically I am building a microsite for a client. I want to pull their fonts from their corporate site to the microsite I will be hosting on a VPS. Currently IE9 is kicking out bugs because of this, and I’m trying to find a good solution. 13. Brodie Thanks for the article. One thing you might want to fix, I noticed the URL to Bootstrap CDN is broken (https://github.com/netdna/bootstrap-cdn/blob/master/.htaccess#L41) Cheers, 14. I am using Nginx 1.4 and the configuration has changed a bit. Instead of an if, a location is preferred. location ~* "^.*?\.(eot)|(ttf)|(woff)$" {
}

15. Ryan

For Apache I would recommend this to help prevent internal server error:

Header set Access-Control-Allow-Origin "*"
16. You say “List specific domains by comma if you want to serve fonts up to only specific domains.” but you should not use a comma separated list here.

The spec only seems to allow for a space separated list or “*”, but it also notes “In practice the origin-list-or-null production is more constrained. Rather than allowing a space-separated list of origins, it is either a single origin or the string “null”.”
See:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Origin

17. For allowing multiple domains see tips at: http://stackoverflow.com/a/1850482/1362634

18. hello
The Google Fonts is not this displaying in my website.
I used this code but did work, i got (500 internal server error)

I checked for a solution in forums and blogs but it looks like nobody can resolve this issue.
I’m using Hostgator and Cloudflare

19. nginx 1.6.* and versions above have deprecated variable: $filename you may replace it with variable: $request_uri

if ($request_uri ~* ^.*?\.(eot)|(ttf)|(woff)$) {
}

20. Thanks for the code. You saved me!

21. Douglas

Assuming http://www.example.com & cdn.example.com are different web servers. Which is appropriate

1) cdn responds with Access-Control-Allow-Origin www.example.com;
2) www response with Access-Control-Allow-Origin cdn.example.com;

22. On Hiawatha webserver in VirtualHost sections :

VirtualHost {
...
...
}

23. HK

Im running nginx and ive tried adding above to htaccess file however I get a 500 internal server error no matter what I try. Any ideas to fix this so that the icons show up on my wordpress site?

Thanks.

24. You should add woff2 to the regexp.

• @clemos – thanks. Got it working now!

25. Steve

Thanks for the help. Saved a lot of time troubleshooting a mess. I know this was written over a year ago. Looks like webkit finally caught up and now no longer supports CDN fonts.

26. Since we are checking via curl, might as well confirm the server is in fact sending in compressed format

curl -I -H 'Accept-Encoding: gzip,deflate'

if you do not see gzip or deflate in the response, then you have some more fun quick fixes to make.

27. Thank you very much for this

28. Paulie-D

For Microsoft IIS7, merge the below into your web.config file located at the root of your application or site (eg: WWWROOT):

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<httpProtocol>
</httpProtocol>
</system.webServer>
</configuration>


If you have a web.config file, merely insert the missing section (eg: customHeaders). Be sure to include the ENTIRE section block.

If you don’t have a web.config file already, simply create a new file called “web.config” in the wwwroot directory with the above contents.

29. Not wanting to be a hater or anything but it’s worth a note of caution here. It’s generally considered bad security practice to load assets from 3rd parties as you have no control over them so if one of those 3rd parties was compromised, you might end up serving malware etc. Just worth bearing in mind is all I’m saying.

30. Paulie-D

@Neil, agreed. However, in my client’s case, they have a WordPress site with a single domain. The client leverages Vimeo PRO account where their videos are hosted and “included” into the WordPress site via Vimeo APIs. Moreover, the client’s site leveraged Google Fonts.

All was well, until we added a second domain (alias). Neither the Vimeo videos would load nor did the Google Fonts work, until I added the aforementioned (my prior post) enhancement to the web.config file.

31. subzey
^.*?\.(eot)|(ttf)|(woff)$ Nginx RegExp is quite strange, it gives false positives. Are you sure you didn’t mean: ^.*?\.(eot|ttf|woff)$

?

32. This is just perfect!

Please add how to purge the files from amazon cloudfront (/* for all files, etc) so that it helps people!

And don’t forget to clear the cache before testing any change!

33. My server is nginx, I don’t think it works for me from any of them..
It’s wordpress site with mutiple language plugin polylang. default domain is english page, sub domain is for foreign.
Could you please pass me the ultimate code?

I got this 500 errors.
Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

• scat

Did you ever get this resolved? I have the exact some issue:

polylang +ngix
34. Great post, thanks.
You may consider to fix the broken mime type of the font. For Apache web-server use:

AddType application/font-woff2    .woff2


To fix mime type on Nginx or using PHP see:
http://zinoui.com/blog/cross-domain-fonts

35. Rachel

This should work for wildcarding subdomains for apache’s httpd.conf:

SetEnvIf Origin "http(s)?://(.*\.yourdomain\.com)$" AccessControlAllowOrigin=$0


…just in case you want to explicitly allow from a single domain instead of *

36. Denisse

Thank you so much for this ! it was really helpful.

37. jordan

If I’m a total newb and don’t know how to access my header file, what should I do? The site I’m working on is a simple WordPress theme. (Fonts are loading with you visit http://lsva.org, which is actually being redirected.)
Thanks!

38. Thank you thank you thank you! Worked perfectly!

39. Neeraj

Hi,

We are using https://fast.fonts.net/jsapi/f6de1715-1efa-4a83-b5d7-8784f9c21d4a.js in our code base, but this is breaking our website on IE11 and the same works on other browsers.

Please suggest what can be done in this case.

Thanks,
Neeraj

40. Thank you David! Worked perfectly!

41. Eugene

This is a good article, but REALLY doesn’t help if you have no idea what you’re doing. Maybe have another article for noobs? thanks.

42. Darren

If you don’t know what your doing you should NOT be wildcarding Access-Control-Allow-Origin as it is a security vulnerability. See http://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html

43. jww

If you see Access-Control-Allow-Origin: * in the response, you’re golden!

This same strategy is used by Bootstrap CDN, so you know it’s good!

Access-Control-Allow-Origin: * seems kind of dangerous. If effectively breaks same origin protections. Shouldn’t the advice be to limit the control to the origin needed?

For example, I don’t need Access-Control-Allow-Origin: * for MathJax and its webfonts located at https://cdnjs.cloudflare.com/ajax/libs/mathjax . Instead, I should use something like:

    Header set Access-Control-Allow-Origin "https://cdnjs.cloudflare.com/ajax/libs/mathjax"

44. jq4321

Could it be that your apache code is slightly wrong? It works, but I think it is more permissive than it should. With

.(eot|ttf|otf|woff)


I think the dot means “any number of characters” which end with eot, etc.

Shouldnt the dot be escaped, like so?

\.


so that it means “any sequence of characters that end with .eot” etc.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!