# 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:

## 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)$){
}

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

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

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)$" { add_header Access-Control-Allow-Origin *; }  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: http://www.w3.org/TR/cors/#access-control-allow-origin-response-header 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 Any genius here could help,please 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)$) { add_header Access-Control-Allow-Origin *; }  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 { ... CustomHeader = Access-Control-Allow-Origin: * ... }  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> <customHeaders> <add name="Access-Control-Allow-Origin" value="*" /> </customHeaders> </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. Please contact the server administrator, postmaster@hermadeals.com and inform them of the time the error occurred, and anything you might have done that may have caused the error. More information about this error may be available in the server error log. • 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!