亚马逊 S3 跨域资源共享(CORS)和 Firefox 跨域字体加载

142

Firefox在加载与当前网页不同源的字体时存在长期问题。通常,这个问题会在使用CDN提供字体时出现。

其他问题中提出了各种解决方案:

CSS @font-face在Firefox中无法正常工作,但在Chrome和IE中可以

随着Amazon S3 CORS的推出,是否有使用CORS解决Firefox字体加载问题的解决方案?

编辑:能够查看S3 CORS配置示例将是很好的。

编辑2:我找到了一个可行的解决方案,但并不真正理解它是如何起作用的。如果有人能够提供更详细的关于配置和Amazon对配置解释中发生的背景魔术的解释,我们将不胜感激,就像nzifnab为此提供了奖励。

13个回答

152

2014年9月10日更新:

现在Cloudfront已经正确支持CORS,因此您不需要再使用下面的查询字符串技巧。有关更多信息,请参见http://aws.amazon.com/blogs/aws/enhanced-cloudfront-customization/和这个答案:https://dev59.com/t2ct5IYBdhLWcg3wQ7My#25305915


好的,我最终使用下面的配置使字体正常工作,稍微从文档示例中进行了一些调整。

我的字体托管在S3上,但由Cloudfront前端管理。

我不确定为什么它起作用了,我的猜测可能是需要<AllowedMethod> GET<AllowedHeader> Content-*

如果精通Amazon S3 CORS配置的人能对此提供一些指导,将不胜感激。

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>https://mydomain.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Content-*</AllowedHeader>
        <AllowedHeader>Host</AllowedHeader>
    </CORSRule>
    <CORSRule>
        <AllowedOrigin>https://*.mydomain.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Content-*</AllowedHeader>
        <AllowedHeader>Host</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

编辑:

有些开发人员遇到了Cloudfront缓存Access-Control-Allow-Origin头文件的问题。AWS工作人员在下面的链接(https://forums.aws.amazon.com/thread.jspa?threadID=114646)中给出了解决方案,由@Jeff-Atwood评论。

从链接的帖子中,建议使用查询字符串作为一种解决方法,以区分来自不同域的请求。我将在此处复制缩短的示例。

使用curl检查响应头:

域A: a.domain.com

curl -i -H "Origin: https://a.domain.com" http://hashhashhash.cloudfront.net/font.woff?https_a.domain.com

来自域名A的响应头:

Access-Control-Allow-Origin: https://a.domain.com
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000
Access-Control-Allow-Credentials: true
X-Cache: Miss from Cloudfront

域名B:b.domain.com

curl -i -H "Origin: http://b.domain.com" http://hashhashhash.cloudfront.net/font.woff?http_b.domain.com

来自域名 B 的响应标头:

Access-Control-Allow-Origin: http://b.domain.com
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000
Access-Control-Allow-Credentials: true
X-Cache: Miss from Cloudfront
你会注意到 Access-Control-Allow-Origin 返回了不同的值,这些值已经通过 Cloudfront 缓存。

2
你是否遇到过类似于这里描述的问题 - 当通过不同的子域名进行后续请求时,Access-Control-Allow-Origin头被缓存并使CORS失效? - Oleg
3
在单个CORSRule元素内可以有多个AllowedOrigin元素,因此您可以将这些CORSRules合并为一个元素,因为它们中的其他元素是相同的。 - Ben Hull
4
如果S3存储桶由CloudFront提供服务,根据亚马逊官方回答的文档,答案就是 根据域名变化字体查询字符串。相关文档链接:https://forums.aws.amazon.com/thread.jspa?threadID=114646 - Jeff Atwood
2
这是一个非常令人沮丧的问题。好消息是,S3现在似乎正在做正确的事情,所以至少可以通过CloudFront提供除Web字体以外的所有内容,并直接从S3提供字体文件。不幸的是,在我们的应用程序中,查询字符串hack并不是很实用,因为所有资产都通过Rails资产管道提供,并且没有方便的方法在请求时调整资产URL(它们都是在部署期间生成的,当资产预编译时)。字体在CSS中的URL已经上载到S3上了。 - Zach Lipton
1
我能够通过明确设置<AllowedOrigin/>头部来使其工作。当我们使用通配符时,即使在cloudfront上使文件无效后,头部也不会被包含在内。我不需要使用querystring选项。 - Jim Wrubel
显示剩余8条评论

104

经过一些调整,我似乎已经成功地让它工作了,而不需要查询字符串 hack。更多信息请参见:http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorS3Origin.html#RequestS3-cors

我将逐步介绍整个设置过程,以便易于查看我所做的事情,希望这对其他人有所帮助。

背景信息: 我正在使用一个带有 asset_sync gem 的 Rails 应用程序,将资产放入 S3 中。其中包括字体文件。

在 S3 控制台中,我点击了我的 bucket、属性和“编辑 cors 配置”,如下图所示:CORS config button

在文本区域内,我有如下内容:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>https://*.example.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

接下来,在Cloudfront面板(https://console.aws.amazon.com/cloudfront/home)中,我创建了一个分发,添加了一个指向我的S3存储桶的源地址。 adding an origin

然后为默认路径添加了一个行为,以指向我设置的基于S3的源地址。 我还点击了“白名单标头”并添加了 Originadding a behavior and whitelist headers

现在发生的情况是我认为是正确的:

1)检查S3标头是否被正确设置。

curl -i -H "Origin: https://example.com" https://s3.amazonaws.com/xxxxxxxxx/assets/fonts/my-cool-font.ttf
HTTP/1.1 200 OK
x-amz-id-2: Ay63Qb5uR98ag47SRJ91+YALtc4onRu1JUJgMTU98Es/pzQ3ckmuWhzzbTgDTCt+
x-amz-request-id: F1FFE275C0FBE500
Date: Thu, 14 Aug 2014 09:39:40 GMT
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000
Access-Control-Allow-Credentials: true
Vary: Origin, Access-Control-Request-Headers, Access-Control-Request-Method
Cache-Control: public, must-revalidate, proxy-revalidate, max-age=180
Last-Modified: Mon, 09 Dec 2013 14:29:04 GMT
ETag: "98918ee7f339c7534c34b9f5a448c3e2"
Accept-Ranges: bytes
Content-Type: application/x-font-ttf
Content-Length: 12156
Server: AmazonS3

2) 检查Cloudfront是否与标头一起工作

curl -i -H "Origin: https://example.com" https://xxxxx.cloudfront.net/assets/fonts/my-cool-font.ttf
HTTP/1.1 200 OK
Content-Type: application/x-font-ttf
Content-Length: 12156
Connection: keep-alive
Date: Thu, 14 Aug 2014 09:35:26 GMT
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000
Access-Control-Allow-Credentials: true
Cache-Control: public, must-revalidate, proxy-revalidate, max-age=180
Last-Modified: Mon, 09 Dec 2013 14:29:04 GMT
ETag: "98918ee7f339c7534c34b9f5a448c3e2"
Accept-Ranges: bytes
Server: AmazonS3
Vary: Origin
X-Cache: Miss from cloudfront
Via: 1.1 77bdacfea247b6cbe84dffa61da5a554.cloudfront.net (CloudFront)
X-Amz-Cf-Id: cmCxaUcFf3bT48zpPw0Q-vDDza0nZoWm9-_3qY5pJBhj64iTpkgMlg==

(请注意,上述情况是由于CloudFront的错误,因为这些文件被缓存了180秒,但相同的文件在命中时可以正常工作)

3)使用不同的来源命中CloudFront(但该来源在S3存储桶的CORS中被允许)- Access-Control-Allow-Origin未被缓存!耶!

curl -i -H "Origin: https://www2.example.com" https://xxxxx.cloudfront.net/assets/fonts/my-cool-font.ttf
HTTP/1.1 200 OK
Content-Type: application/x-font-ttf
Content-Length: 12156
Connection: keep-alive
Date: Thu, 14 Aug 2014 10:02:33 GMT
Access-Control-Allow-Origin: https://www2.example.com
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000
Access-Control-Allow-Credentials: true
Cache-Control: public, must-revalidate, proxy-revalidate, max-age=180
Last-Modified: Mon, 09 Dec 2013 14:29:04 GMT
ETag: "98918ee7f339c7534c34b9f5a448c3e2"
Accept-Ranges: bytes
Server: AmazonS3
Vary: Origin
X-Cache: Miss from cloudfront
Via: 1.1 ba7014bad8e9bf2ed075d09443dcc4f1.cloudfront.net (CloudFront)
X-Amz-Cf-Id: vy-UccJ094cjdbdT0tcKuil22XYwWdIECdBZ_5hqoTjr0tNH80NQPg==

请注意,上面的域名已成功更改,而没有使用查询字符串黑客技巧。

当我更改Origin头时,第一次请求似乎总会有一个X-Cache: Miss from cloudfront,然后之后我获得了预期的X-Cache: Hit from cloudfront

附:值得注意的是,当使用curl -I(大写的I)时,不会显示Access-Control-Allow-Origin头,因为它只是一个HEAD,我使用-i将其变成GET并向上滚动。


1
其他人都没解决的问题,它却起作用了。感谢你花时间发布如此详细的内容! - iwasrobbed
它运行了!!顺便说一下 - 在测试时,我得到了一个巨大的HTTP响应文本...将编辑答案以使用此curl解决方案...https://dev59.com/yGkw5IYBdhLWcg3wNXz2 - Michael Gorham
1
+1 表示添加客户端头 Origin,Cloudfront 将根据该头缓存对象(并将服务器 CORS 头信息转发给用户)。 - Sébastien Saunier
我建议使用Advanced Rest Client Chrome扩展程序(https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo?hl=en-US)来查看头部信息,它能够很好地将内容与头部分离,使得你不必像在终端中那样频繁滚动。 - Tamik Soziev
就这么简单! 只需将“Origin”标头加入白名单即可。谢谢Eamonn。 - webStuff
显示剩余6条评论

13

直到最后一次向Heroku推送,我的字体都被正确地加载...我不知道为什么,但CORS允许来源中的通配符停止工作了。我在存储桶设置中添加了所有的prepro和pro域名到CORS策略中,现在它看起来像这样:

<CORSConfiguration>
    <CORSRule>
        <AllowedOrigin>http://prepro.examle.com</AllowedOrigin>
        <AllowedOrigin>https://prepro.examle.com</AllowedOrigin>
        <AllowedOrigin>http://examle.com</AllowedOrigin>
        <AllowedOrigin>https://examle.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Authorization</AllowedHeader>
    </CORSRule>

</CORSConfiguration>

更新:也请添加您的http://localhost:端口号


1
感谢您分享这个解决方案。这对我很有用。 - rmontgomery429

9
在Amazon S3中,CORS配置(S3 Bucket / Permissions / CORS)如果您使用以下内容:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>

CORS对于Javascript和CSS文件非常有效,但它不适用于字体文件

您必须使用@VKen答案中表达的模式指定允许CORS的域:https://dev59.com/t2ct5IYBdhLWcg3wQ7My#25305915

因此,请使用以下内容:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
<CORSRule>
    <AllowedOrigin>https://*.mydomain.com</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

请记得将“mydomain.com”替换为您的域名。

完成后,使CloudFront缓存失效(CloudFront / 失效 / 创建失效),然后它就可以工作了。


8

据文档所述,您可以将配置作为“存储桶中的 CORS 子资源”来设置。我理解为在存储桶的根目录下创建一个名为“cors”的文件,并在其中添加配置,但这种方式行不通。最终,我需要登录到 Amazon S3 管理区域,并在我的存储桶的 属性 对话框中添加配置。

Amazon S3 的文档还有待改进...


1
是的,但我很幸运在属性面板上发现了一些新的界面更改。我一直在编辑存储桶策略,所以自然而然地在同一面板中寻找CORS配置。 - VKen
对我很有用,我想在我的应用程序中设置它,谁知道它会这么简单。 - Richlewis

6
在我的情况下,我没有在CORS配置中定义XML命名空间和版本。定义这些后问题得到解决。
更改过的内容。
<CORSConfiguration>

to

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">

对我也有效。我的字体托管在存储桶本身上。 - khamaileon
为什么默认模板不自动包含这个我无法理解。 - CoatedMoose

6

有更好、更简单的方法!

我个人喜欢使用我的DNS子域名来解决这个问题。如果我的CDN位于cdn.myawesomeapp.com而不是sdf73n7ssa.cloudfront.net后面,那么浏览器就不会因为跨域安全问题而出现错误。

要将您的子域名指向AWS Cloudfront域,请转到AWS Cloudfront控制面板,选择Cloudfront分发并将CDN子域名输入到“备用域名(CNAME)”字段中。例如,类似于cdn.myawesomeapp.com的内容。

现在,您可以转到您的DNS提供商(如AWS Route 53)并创建一个CNAME,将cdn.myawesomeapp.com指向sdf73n7ssa.cloudfront.net。

http://blog.cloud66.com/cross-origin-resource-sharing-cors-blocked-for-cloudfront-in-rails/


这会破坏SSL,或者说使用SSL会花费很多钱,因此很多人不这样做。 - maletor

4
这个配置对我很有用。我可以列出对象,检索、更新和删除它们。
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <CORSRule>
    <AllowedOrigin>http://localhost:3000</AllowedOrigin>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
    <ExposeHeader>ETag</ExposeHeader>
    <ExposeHeader>x-amz-meta-custom-header</ExposeHeader>
  </CORSRule>
</CORSConfiguration>

你需要更改域名,因为我是从本地主机测试的。只需查看此页面以获取CORS信息:http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/browser-configuring.html - Shahid

3

2021年的解决方案,不会因为在AllowedDomains中允许"*"而危及安全。

步骤1)允许S3使用CORS

S3 bucket > Permissions > Cross-origin resource sharing (CORS)中,在AllowedOrigins中添加您的域名/域名列表。有关示例,请参见官方文档。对于AllowedMethods,只需要GET

步骤2)告知CloudFront发送CORS标头

在您的CloudFront Behavior < Origin Request Policy中,确保选择一个发送 originaccess-control-request-headers 标头的策略,例如 Managed-CORS-S3Origin

enter image description here

步骤3)[可选,仅适用于多个域名的情况]

请参见我关于如何处理S3+CloudFront的多个域名的答案

步骤4)使您的CloudFront分发失效

祝你好运!


1
AWS控制台在S3 Bucket的CORS配置中不接受XML,只接受JSON。 - Gustavo Contreiras
1
它对我起作用了,但我需要将“Origin request policy”设置为“CORS-S3Origin”,并且我需要将“Response headers policy”设置为“CORS-With-Preflight”。 - Matt Kleinsmith

1
<ifModule mod_headers.c>

   Header set Access-Control-Allow-Origin: http://domainurl.com

</ifModule>

简单解决方案

谢谢分享!这给了我一个想法,可以在上传静态资源到云存储时将此标题作为“元数据”添加。(虽然这样只能适用于1个特定域或所有域) - Vinay Vissh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接