从 AWS Cloudfront 提供服务时发出加载 webfonts

Issue loading webfonts when served from AWS Cloudfront

所以,这是我的难题!在过去的三天里,我一直在努力让 Cloudfront 与我的 nginx 服务器配合得很好……阅读了无数 Whosebug posts 和博客文章……搜索了互联网,但我仍然被围绕 cross-domain 关于 Cloudfront 服务字体的访问策略。我将 post 我的完整设置,希望有更多专业知识的人可以帮助我弄清楚发生了什么。将来,我希望这个 post 能为许多面临类似问题的人服务。 这里...

Nginx 配置:

我有一个具有以下服务器块配置的 nginx 网络服务器。 (...为简洁起见被截断)

    server {
        server_name  example.com www.example.com;
        root /var/www/example.com/html;
        index index.html index.htm;

        location / {
            try_files $uri $uri/ =404;
        }
        location /assets {
            autoindex on;
        }

        # Media
        location ~* \.(jpe?g|gif|png|ico|cur|gz|svgz?|mp4|ogg|ogv|webm|htc|webp)$ {
            expires 1M;
            access_log off;
            add_header Cache-Control public;
        }

        # Fonts
        location ~* \.(eot|ttf|woff|woff2|svg)$ {
            expires 365d;
            access_log off;
            add_header Cache-Control public;
            add_header Access-Control-Allow-Origin example.com;
            // Have also tried setting the "Access-Control-Allow-Origin" header to "*", but I'd prefer not to do this for security reasons.
        }

    }

仅供参考:我想提供并卸载到 CloudFront 的所有网站文件都在我的虚拟主机的 /assets 目录中。 (即http://example.com/assets/.。)


CloudFront:

我使用以下设置创建了一个新的 CloudFront 分配:

注意:我没有使用 S3 来托管我的网站文件和资产。

一般:

起源:

行为:


DNS 设置:

我的区域文件的一部分...

example.com. 1800 IN A 12.34.567.890  //faked IP here for privacy reasons
www.example.com. 1800 IN CNAME example.com.
static.example.com. 1800 IN CNAME kg72kgf83nhfy3.cloudfront.net.  //faked CloudFront dist. domain name here for privacy reasons

发生了什么事?为什么不行?

因此,CloudFront 会处理和部署我的分发,我假设从我的 '/assets/..'[ 中提取资产=156=]网页目录。所有 srchref 和 CSS url() 引用都指向 http://static.example.com in my current HTML and CSS documents, including all font-face references. After the distribution is deployed, I hit my site http://example.com 在浏览器中。

似乎所有静态站点资产都从 CloudFront 正确提供,带有适当的缓存 headers,如我的 nginx 配置中所定义...EXCEPT...网络字体。我在页面上缺少字体,并且在我的浏览器控制台中收到 cross-domain 访问策略错误消息。

Headers—

  • 一张图片,供参考(ping我的服务器时):

    curl -I "http://example.com/assets/images/image1.png"
    HTTP/1.1 200 OK
    Server: nginx/1.6.3
    Date: Sun, 15 May 2016 02:09:25 GMT
    Content-Type: image/png
    Content-Length: 194665
    Last-Modified: Sun, 15 May 2016 01:52:35 GMT
    Connection: keep-alive
    ETag: "5737d663-2f869"
    Expires: Tue, 14 Jun 2016 02:09:25 GMT
    Cache-Control: max-age=2592000
    Cache-Control: public
    Accept-Ranges: bytes
    
  • 一种字体(当 ping 我的服务器时):

    curl -I "http://example.com/assets/fonts/webfont.woff"
    HTTP/1.1 200 OK
    Server: nginx/1.6.3
    Date: Sun, 15 May 2016 02:10:29 GMT
    Content-Type: application/font-woff
    Content-Length: 8752
    Last-Modified: Sun, 15 May 2016 01:51:55 GMT
    Connection: keep-alive
    Vary: Accept-Encoding
    ETag: "5737d63b-2230"
    Expires: Mon, 15 May 2017 02:10:29 GMT
    Cache-Control: max-age=31536000
    Cache-Control: public
    Access-Control-Allow-Origin: example.com
    Accept-Ranges: bytes
    
  • 相同图像(从 CloudFront 请求时):

    curl -I "http://static.example.com/assets/images/image1.png"
    HTTP/1.1 200 OK
    Content-Type: image/png
    Content-Length: 194665
    Connection: keep-alive
    Server: nginx/1.6.3
    Date: Sun, 15 May 2016 02:39:16 GMT
    Last-Modified: Sun, 15 May 2016 01:52:35 GMT
    ETag: "5737d663-2f869"
    Expires: Tue, 14 Jun 2016 02:39:16 GMT
    Cache-Control: max-age=2592000
    Cache-Control: public
    Accept-Ranges: bytes
    X-Cache: Miss from cloudfront
    Via: 1.1 lots_of_random_characters_i_dont_know_if_should_share_here.cloudfront.net (CloudFront)
    X-Amz-Cf-Id: lSM1plINYENbYycBn424LJ2wdtDhS3CpqAFiDSoxQDEctP_WM09bUQ==
    
  • 相同字体(从 CloudFront 请求时):

    curl -I "http://static.example.com/assets/fonts/webfont.woff"
    HTTP/1.1 200 OK
    Content-Type: application/font-woff
    Content-Length: 8752
    Connection: keep-alive
    Server: nginx/1.6.3
    Date: Sun, 15 May 2016 02:41:00 GMT
    Last-Modified: Sun, 15 May 2016 01:51:55 GMT
    ETag: "5737d63b-2230"
    Expires: Mon, 15 May 2017 02:41:00 GMT
    Cache-Control: max-age=31536000
    Cache-Control: public
    Access-Control-Allow-Origin: example.com
    Accept-Ranges: bytes
    Vary: Accept-Encoding
    X-Cache: Miss from cloudfront
    Via: 1.1 lots_of_random_characters_i_dont_know_if_should_share_here.cloudfront.net (CloudFront)
    X-Amz-Cf-Id: vNfiyurS8pjosofnpLNSrnZuaGFg0V4xIs4ySCm05NKDMZ_PozhuOg==
    

http://example.com 加载我的网站,一切似乎都正常(即图像)EXCEPT 网络字体。检查浏览器控制台为每种字体输出以下消息:

Font from origin 'http://static.example.com' has been blocked from loading by Cross-Origin Resource Sharing policy: The 'Access-Control-Allow-Origin' header contains the invalid value 'example.com'. Origin 'http://example.com' is therefore not allowed access.

那么,有人有什么想法吗??我会 非常 感谢 help/input。我是一名正在努力学习的年轻 Web 开发人员。

谢谢! :)
–凯尔



脚注:

  • One of many blog posts我已经关注了,但还没有成功。
  • 我的计划是将散列查询字符串附加到每个文件的末尾(即 //static.example.com/assets/images/image.png?622c6911)以使 CloudFront 缓存无效。这样我就不必总是 re-upload 名称更改的新资产...我可以简单地控制 HTML 的失效并在我希望 CloudFront 请求最新版本时将新的查询字符串附加到资产来自我的网络服务器 Origin 的那个文件。
  • 我最终会为我的网站购买 SSL 证书,所以我希望流量能够同时支持 HTTP 和 HTTPS 请求。

解释在这里:

The 'Access-Control-Allow-Origin' header contains the invalid value 'example.com'. Origin 'http://example.com' is therefore not allowed access.

来源是 http://example.com... 不是 example.com。您的回复 header 的值有误。根据定义,来源是方案 + 主机名 + 端口(标准端口上的 http 和 https 省略了隐式端口 80 和 443)。

当请求不受 cross-origin 规则的约束时,例如图像的情况,浏览器将忽略 Web 服务器上的这种错误配置。对于字体,您正在撞墙,因为值格式不正确。

add_header Access-Control-Allow-Origin example.com;

这是你问题的根源。您需要在 Origin: header 中使用浏览器发送的相同来源进行响应,假设该来源确实有效并且应该被允许。我不是 nginx 专家,根据 this answer,您可以使用正则表达式验证传入来源,并相应地设置响应:

if ($http_origin ~* "^https?://.*example\.com$" ) {
    add_header Access-Control-Allow-Origin $http_origin;
}

不熟悉 nginx 正则表达式的怪癖,我的有点宽容,但你明白了。

在此之前,我们需要修复 CloudFront 缓存行为。

Forward Headers: Whitelist

Access-Control-Allow-Origin

那不是一个请求 header...那是一个响应 header,所以将它列入白名单实际上并没有做任何事情。

相反,您需要将 Origin header 列入白名单,以便 CloudFront 将其转发到 Web 服务器,以便服务器可以使用 [=] 中的相同值进行响应16=],如上图所示。

Access-Control-Request-HeadersAccess-Control-Request-Method 也应该列入白名单,但对于 GET 请求,我不知道它们是否重要。

除了 GETHEAD.

之外,您还应该修改允许的方法以包括 OPTIONS

奖金material:

My plan is to append hashed query strings to the end of every file (ie. //static.example.com/assets/images/image.png?622c6911) to invalidate the CloudFront cache.

在这种情况下,您需要将 Forward Query Strings 设置为 Yes

CloudFront 根据它实际发送到服务器的内容缓存 objects。如果查询字符串未转发到源,则附加查询字符串只会使 浏览器 缓存无效,而不会使 CloudFront 缓存无效。是的,如果查询字符串旨在用于 cache-busting CloudFront,即使您的服务器不需要或不需要查询字符串,您也需要启用此功能。这就是为什么不转发它们 "improves caching." CloudFront 将 "improved" 在确定它是否已经具有 object.

的缓存版本时完全忽略它们

此外,请注意 Via header 不包含敏感信息,X-Amz-Cf-Id.

也不包含敏感信息