根本原因是@font-face加载跨域字体文件时,服务端未返回Access-Control-Allow-Origin响应头,且CSS中未声明crossorigin: anonymous,导致浏览器静默拦截woff2等字体资源,仅显示方块或问号。

直接原因不是 CSS 跨域,而是字体文件(woff2/woff/ttf)在加载时被浏览器静默拦截——@font-face 里的 src 指向了跨域地址,但服务端没返回 Access-Control-Allow-Origin 响应头,浏览器就丢掉字体数据,不报错、不渲染,只显示方块或问号。
为什么对字体没用
CSS 文件本身跨域加载失败是“静默失效”:样式表不生效,但控制台通常没错误;而字体是另一个独立资源请求,走的是 @font-face 的 src,和 <link> 的 crossorigin 属性完全无关。即使你给 <link> 加了 crossorigin,它只影响 CSS 文件本身的加载行为(比如让失败触发 onerror),不影响里面 @font-face 发起的字体请求。
-
<link rel="stylesheet" href="https://cdn.example.com/style.css" crossorigin>—— 这个crossorigin不会传递给style.css里写的url("https://cdn.example.com/icon.woff2") - 字体请求是否启用 CORS,只取决于
@font-face规则里有没有crossorigin: anonymous - 没有这行,浏览器默认以非 CORS 模式发请求,哪怕服务端配了 header,也白搭
CDN 字体路径能访问但图标不显示?检查三个硬条件
就算你在浏览器地址栏输入 https://cdn.example.com/iconfont.woff2 能下载成功,也不代表前端能用。必须同时满足:
- CDN 域名响应中包含
Access-Control-Allow-Origin: https://yourdomain.com(不能只写*,如果页面带 cookie 或后续要配凭证) - CSS 中每个
@font-face都显式声明crossorigin: anonymous(很多 iconfont.cn 下载的 CSS 默认没有这一行,得手动补) - 字体文件真实存在且路径与 CSS 中
url()完全一致(注意相对路径基准是 CSS 文件位置,不是 HTML)
为什么安卓报 loadFontFace:fail 而 iOS 正常
安卓 WebView 对 CORS 的执行更严格:iOS 可能宽松放行部分跨域字体请求,安卓则坚决拦截。典型现象是 uni-app 或微信小程序里 uni.loadFontFace 返回 fail,但控制台无明确 CORS 报错。这时重点查服务端响应头是否对 .woff2 等后缀返回了 Access-Control-Allow-Origin,且 Nginx 配置用了 always 标志(否则 404/304 响应不带 header)。
立即学习“前端免费学习笔记(深入)”;
- Nginx 必须用
location ~* \.(woff2?|ttf|eot)$ { add_header ... always; },不能只写add_header不加always - CDN 用户需确认是否透传源站 header;部分 CDN(如早期 jsDelivr)对 font 路径有缓存误判,加
?v=1可绕过 - 别信“字体文件能下载 = 前端能用”,CORS 是运行时校验,和人工访问无关
最容易被忽略的一点:即使你把所有配置都做对了,只要有一处 @font-face 漏了 crossorigin: anonymous,或者某一个字体格式(比如只留了 .woff2)在旧环境里 fallback 失败,图标就会断在那一环——它不报错,只沉默消失。


















