When loading web fonts from a subdomain to your main domain, browsers enforce Cross-Origin Resource Sharing (CORS) policies. NGINX needs explicit headers to permit this resource sharing. Here's how to solve it properly.
For most cases, adding these directives to your NGINX server block will work:
location ~* \.(eot|ttf|woff|woff2)$ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type";
expires 365d;
}
For better security, replace the wildcard (*) with specific domains:
location ~* \.(eot|ttf|woff|woff2)$ {
add_header Access-Control-Allow-Origin "https://your-main-domain.com";
add_header Access-Control-Allow-Methods "GET, OPTIONS";
add_header Access-Control-Max-Age 3600;
add_header Access-Control-Allow-Credentials "true";
add_header Cache-Control "public";
expires max;
}
For OPTIONS preflight requests, add this separate location block:
location / {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin "https://your-main-domain.com";
add_header Access-Control-Allow-Methods "GET, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type";
add_header Access-Control-Max-Age 3600;
add_header Content-Type 'text/plain charset=UTF-8';
add_header Content-Length 0;
return 204;
}
}
After making changes, always test with:
sudo nginx -t
sudo systemctl reload nginx
Then check headers using browser dev tools or curl:
curl -I https://your-subdomain.com/path/to/font.woff2
1. Multiple add_header directives: Only the last one takes effect unless you use the 'always' parameter
2. Cache poisoning: Always pair CORS headers with proper Cache-Control
3. Protocol mismatches: Ensure http/https consistency between domains
For complex setups with multiple allowed domains:
map $http_origin $cors_origin {
default "";
"~^https://(main\.domain\.com|sub\.domain\.com)$" $http_origin;
}
server {
...
add_header Access-Control-Allow-Origin $cors_origin always;
...
}
When serving web fonts from a subdomain to your main domain, browsers enforce Cross-Origin Resource Sharing (CORS) policies. Without proper headers, you'll encounter errors like "Font from origin has been blocked by CORS policy".
Here's the minimal configuration to enable CORS for fonts in your NGINX server block:
location ~* \.(eot|ttf|woff|woff2)$ {
add_header Access-Control-Allow-Origin *;
}
For better security, specify allowed domains instead of using wildcard (*):
location ~* \.(eot|ttf|woff|woff2)$ {
add_header 'Access-Control-Allow-Origin' 'https://yourmaindomain.com';
add_header 'Access-Control-Allow-Methods' 'GET';
add_header 'Access-Control-Max-Age' 86400;
}
To allow multiple domains, use a map directive:
map $http_origin $cors_origin {
default "";
"~^https://(domain1\.com|domain2\.com)$" $http_origin;
}
server {
...
location ~* \.(eot|ttf|woff|woff2)$ {
if ($cors_origin) {
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Vary' 'Origin';
}
}
}
After making changes, test with:
curl -I https://yoursubdomain.com/fonts/yourfont.woff2
Look for the Access-Control-Allow-Origin header in the response.
Problem: Headers not appearing in response
Solution: Ensure your location block matches the font file paths exactly
Problem: Chrome still blocks fonts
Solution: Add the Vary header and clear browser cache
For optimal performance with CORS fonts:
- Use WOFF2 format where possible
- Set appropriate cache headers
- Consider using a CDN for font delivery