Font from origin ‘https://xxx.cloudfront.net’ has been blocked from loading by Cross-Origin Resource Sharing policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘https://foobar.com’ is therefore not allowed access.

如果项目里的资源文件使用时候都遵守规范,加上 asset_host 的支持,把Rails 的项目配置 CDN,应该和“把大象装进冰箱”一样简单的。然而遇上不怎么简单的CDN 服务(七牛的 CDN镜像很好用啊,就是域名国内要备案ooxx),有时也会变得很折腾,比如以上引用Chrome浏览器 中的错误描述。

看错误内容,推断出用户访问 foobar.com 网站时候,包含 font 的某个 css 文件在 CDN 上被拒绝访问了,原因是触犯了 Cross-Origin-Resource-Sharing(CORS) 策略,提示要从 Access-Control-Allow-Origin 设定去入手解决。

OK,我们的 css 里面的确包含了font-awesome等一些第三方的字体文件等,如果 css 已经从 CDN 服务,相对路径的字体文件也是引用当前CDN 地址,但是用户访问我们网站的目标地址,肯定是和 CDN 地址不是同一域名。W3C针对在 CSS 中调用字体的规范中,对此有详细的解释:

The implications of this for authors are that fonts will typically not be loaded cross-origin unless authors specifically takes steps to permit cross-origin loads. Sites can explicitly allow cross-site loading of font data using the Access-Control-Allow-Origin HTTP header. For other schemes, no explicit mechanism to allow cross-origin loading, beyond what is permitted by the potentially CORS-enabled fetch method, is defined or required.

简单地说,字体作为一种特殊的资源格式,默认是不能跨站调用的,除非调用字体的HTTP 请求开启 Access-Control-Allow-Origin 这个header,并且按照W3C 约定的 url 使用规范

了解了问题发生的原因,那么解决问题的思路也有了。回到 CDN 服务,需要找到相关的 CORS 支持。经过漫长的 Google 和搜索过滤,最终在AWS 的官方找到CORS 和自定义域名相关的一句话解释说明

Custom origins – Forward the Origin header along with any other headers required by your origin

如果对 AWS 的服务和 CloudFront 不是很熟悉,这句解释可能不会印象深刻,然而 debug 到这里,各种相关的文章、文档其实已经查了好几遍。Amazon 在2014年中的时候,升级了 CloudFront 服务相关 Header 的优化:

CORS (Cross Origin Resource Sharing) – CloudFront can now be used to deliver web assets such as JavaScript and fonts to other websites. Because CloudFront can now be configured to pass the Origin header along to the origin server, you can now use CORS to allow cross-origin access to your content.

显然,在此之前,在 CloudFront 上解决 CORS 的问题是相当痛苦的。换句话说,目前 CloudFront 可以通过配置 Origin 这个 header 来和服务器源通信,获得资源跨站请求的可能。赶紧打开 Distribution 的配置,Edit 它的 behavior, 添加 Origin 这个 Header:

edit_behavior

然而CloudFront 只是转发 Origin 的 header,源服务器才是打开 Access-Control-Allow-Origin 设置的地方,这里对如何在 Linux 和 HTTP 服务器设置头标签略过,我是 Nginx,添加相关配置如下:

   location ~* \.(eot|ttf|woff|woff2)$ {
       add_header Access-Control-Allow-Origin *;
   }

至此,Web 服务器和 CDN 配置都已经完成,Invalidate 之前对应的CSS Cache,刷新网站错误终于消失了。