必须显式声明crossorigin: anonymous,否则浏览器不走cors流程;漏写会导致字体被同源策略拦截,即使服务端已配access-control-allow-origin头也无效。

crossorigin: anonymous 必须显式声明,否则浏览器不走 CORS 流程
加了 @font-face 但控制台仍报 “Font from origin X has been blocked” —— 很可能只是漏写了 crossorigin: anonymous。这个属性不是可选装饰,而是触发 CORS 请求的开关。没有它,浏览器会以普通非 CORS 方式请求字体,服务端即使返回了 Access-Control-Allow-Origin 头也无效。
常见错误写法:@font-face { font-family: 'IconFont'; src: url('https://cdn.example.com/icon.woff2') format('woff2'); }
→ 缺少 crossorigin: anonymous,请求直接被同源策略拦截。
- 必须写成
crossorigin: anonymous(不能是use-credentials,除非你真要传 cookie) - 即使字体和页面同域,也建议加上——避免开发环境(如 Vite/HMR)或 CDN 缓存策略引发的隐性问题
- Safari 对缺失该声明更敏感,有时静默失败而不报错
服务端响应头必须覆盖所有字体后缀,且带 always 标志
前端加了 crossorigin: anonymous 还报 No 'Access-Control-Allow-Origin' header is present?说明字体服务器没返回正确响应头。这不是前端能绕过的,必须改服务端配置。
关键点不是“加个头”,而是加得准、加得稳:
- 用
location ~* \.(woff2|woff|ttf|eot|otf|svg)$精确匹配字体路径,别只在server块里全局add_header - 必须加
always参数,否则 304 或 404 响应不会带 CORS 头,缓存失效时字体突然不显示 -
Access-Control-Allow-Origin不能设为*如果字体请求带凭证(比如用了crossorigin: use-credentials),得写具体域名,如https://yourapp.com - CDN 用户要确认是否透传源站响应头——很多 CDN 默认不缓存
add_header,需单独开启“缓存响应头”选项
src 中 format() 参数写错会导致 Safari/旧 Chrome 静默失败
路径和跨域都对,但字体就是不渲染?检查 format() 字符串是否拼写正确。浏览器对这个值大小写和内容很敏感,错一个字母就整个 src 条目被忽略。
典型错误:url('font.ttf') format('ttf') → 应为 format('truetype')url('font.woff') format('woff') → 正确,但 woff2 必须写 format('woff2')
- 推荐最小安全集:优先
woff2,降级到woff,再降级到truetype(对应 .ttf) - IE9 及以下才需要
embedded-opentype(对应 .eot),现在基本可删 - 多个
url()之间不能有悬空逗号,例如url(a.woff2), url(b.woff),→ Safari 会直接丢弃整条src
file:// 协议下 @font-face 必然失败,必须起本地服务
用双击打开 HTML 文件测试时字体不显示?不是代码问题,是浏览器限制:file:// 协议下所有字体请求都被视为跨域,且无法设置 CORS 响应头。
- Chrome/Firefox 在
file://下会直接拒绝加载外部字体,控制台可能无报错或只显示 net::ERR_FAILED - 解决方法只有起本地 HTTP 服务:Vite 用
vite preview,Webpack 用webpack serve,Python 用户可用python3 -m http.server 8000 - VS Code 插件 Live Server 是最省事的选择,它自动处理 MIME 类型和基础 CORS(但生产仍需自己配)
真正容易被忽略的是:开发阶段一切正常,上线后因 CDN 或服务端响应头配置遗漏导致部分用户字体空白——这种问题往往只在灰度发布后才暴露,务必在预发环境用真实 CDN 域名 + 真实服务端头做全链路验证。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











