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

Posted

技术标签:

【中文标题】Amazon S3 CORS(跨域资源共享)和 Firefox 跨域字体加载【英文标题】:Amazon S3 CORS (Cross-Origin Resource Sharing) and Firefox cross-domain font loading 【发布时间】:2012-08-27 03:03:32 【问题描述】:

长期以来,Firefox 无法从与当前网页不同的来源加载字体,这是一个长期存在的问题。通常,在 CDN 上提供字体时会出现问题。

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

CSS @font-face not working with Firefox, but working with Chrome and IE

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

编辑:很高兴看到 S3 CORS 配置示例。

edit2:我找到了一个可行的解决方案,但并没有真正理解它的作用。如果有人可以提供有关配置的更详细解释以及亚马逊对配置的解释中发生的背景魔法,我们将不胜感激,就像 nzifnab 为它提供赏金一样。

【问题讨论】:

【参考方案1】:

是的,当然。 Firefox 支持 CORS 字体,就像规范要求 http://dev.w3.org/csswg/css3-fonts/#allowing-cross-origin-font-loading

【讨论】:

感谢您的及时回复,Boris Zbarsky。您能否展示一些 S3 CORS 设置的示例配置? 我从来没有考虑过配置 S3...至于在 HTTP 级别发送什么,如果您可以接受它,只需发送“Access-Control-Allow-Origin: *”在字体文件的 HTTP 响应中应该可以工作。 谢谢,我正在尝试了解如何使用 S3 CORS 配置进行该设置。【参考方案2】:

2014 年 9 月 10 日更新:

由于 Cloudfront 现在正确支持 CORS,因此您不再需要执行以下任何查询字符串破解。有关更多信息,请参阅http://aws.amazon.com/blogs/aws/enhanced-cloudfront-customization/ 和此答案:https://***.com/a/25305915/308315


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

我的字体托管在 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 缓存。

【讨论】:

您是否遇到过类似于here 所述的问题 - 当通过不同的子域发出后续请求时,Access-Control-Allow-Origin 标头会被缓存并使 CORS 无效? @o.v.我没有遇到此问题,因为我明确设置了使用资源的域。我已经阅读了您之前发布的链接。我依稀记得另一个线程上的一些回复说必须明确说明域,因此 * 实际上是不允许的,由于某些限制。我现在找不到那些回复帖子,可能是我在其他地方读到的博客帖子。希望对您有所帮助。 您可以在单个 CORSRule 元素中包含多个 AllowedOrigin 元素,因此您可以将这些 CORSRule 组合成一个元素,因为其中的其他元素是相同的。 @dan 如果 S3 存储桶由 CloudFront 提供服务,看起来答案是 按域改变字体查询字符串,如亚马逊官方回答中所述:forums.aws.amazon.com/thread.jspa?threadID=114646 这是一个非常令人沮丧的问题。好消息是 S3 现在似乎在做正确的事情,因此至少可以通过 CloudFront 提供除 webfonts 之外的所有内容,并直接从 S3 提供字体文件。遗憾的是,如果没有更重要的重构,查询字符串 hack 在我们的应用程序中并不实用,因为资产都是通过 Rails 资产管道提供的,并且没有方便的方法在请求时调整资产 URL(它们都是在部署期间生成的当资产被预编译时)。字体在 css 中的 URL 已经在 S3 上。【参考方案3】:

好吧,文档声明您可以将配置作为“存储桶中的 cors 子资源”。我认为这意味着我将使用配置在我的存储桶的根目录创建一个名为“cors”的文件,但这不起作用。最后我不得不登录到 Amazon S3 管理区域并在我的存储桶的properties 对话框中添加配置。

S3 可以使用一些更好的文档...

【讨论】:

是的,但我很幸运在属性面板中发现了一些新的界面变化。我一直在编辑存储桶策略,所以很自然地我会在同一个面板中寻找 CORS 配置。 为我工作,我想在我的应用程序中设置它,谁知道它会这么简单【参考方案4】:

在我的例子中,我没有在 CORS 配置中定义 XML 命名空间和版本。定义那些有效的。

改变

<CORSConfiguration>

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

【讨论】:

也适合我。我的字体托管在存储桶本身上。 为什么默认模板不自动包含这个我无法理解。【参考方案5】:

经过一些调整,我似乎可以在没有查询字符串破解的情况下使用它。更多信息在这里:http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorS3Origin.html#RequestS3-cors

我将完成整个设置,以便轻松查看我所做的工作,希望这对其他人有所帮助。

背景信息: 我正在使用具有asset_sync gem 的Rails 应用程序将资产放到S3 上。这包括字体。

在 S3 控制台中,我点击了我的存储桶、属性和“编辑 cors 配置”,此处:

在 textarea 里面我有类似的东西:

<?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 存储桶的 Origin

然后为默认路径添加了一个行为,以指向我设置的基于 S3 的原点。我还做的是点击白名单标题并添加Origin

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

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==

(请注意,以上是来自云端的缺失,因为这些文件被缓存了 180 秒,但同样适用于命中)

3) 以不同的来源命中云端(但在 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 (capital I) 时不会显示 Access-Control-Allow-Origin 标头,因为它只是一个 HEAD,我执行 -i 使其成为 GET 并向上滚动。

【讨论】:

在所有其他人都没有的情况下工作。感谢您花时间发布如此详细的信息! 有效!!仅供参考 - 我在测试这个时有一个巨大的 http 响应文本......要编辑答案以使用这个 curl 解决方案......***.com/questions/10060098/… 非常感谢大家 - 很高兴看到它对其他人有用。 我无法告诉您您对我们的帮助有多大! +1 +1 用于添加来自查看器的客户标头 Origin,以便 Cloudfront 根据该标头缓存对象(并将服务器 CORS 标头转发回用户)【参考方案6】:

在最后一次推送到 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:PORT

【讨论】:

感谢您分享此解决方案。这对我有用。【参考方案7】:

有更好更简单的方法!

我个人更喜欢使用我的 DNS 子域来解决这个问题。如果我的 CDN 位于 cdn.myawesomeapp.com 而不是 sdf73n7ssa.cloudfront.net 之后,那么浏览器就不会因为跨域安全问题而崩溃并阻止它们。

要将您的子域指向您的 AWS Cloudfront 域,请转到 AWS Cloudfront 控制面板,选择您的 Cloudfront 分配并将您的 CDN 子域输入到备用域名 (CNAME) 字段中。像 cdn.myawesomeapp.com 这样的东西就可以了。

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

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

【讨论】:

这会破坏 SSL,或者说使用 SSL 需要花费很多钱,因此很多人不这样做。【参考方案8】:
<ifModule mod_headers.c>

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

</ifModule>

简单的解决方案

【讨论】:

感谢分享!让我想到在 上传 静态资产到云存储时将此标头添加为 'meta-data'(虽然这样它只适用于 1 个 particular domainall domains【参考方案9】:

此配置对我有用。我可以列出对象、检索、更新和删除。

<?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>

【讨论】:

您需要更改域,因为我是从 localhost 进行测试的,只需查看此页面的 CORS:docs.aws.amazon.com/AWSjavascriptSDK/guide/…【参考方案10】:

重新启动我的 Spring Boot 应用程序(服务器)为我解决了这个问题。

我在 S3 上正确配置了 CORS。 curl 使用原始标头给出了正确的响应。 Safari 正在正确获取字体。 只有chrome不愿意接受CORS。

不确定究竟是什么导致了这种行为。 一定和If-modified-since有关

【讨论】:

【参考方案11】:

在 Amazon S3 CORS 配置(S3 存储桶/权限/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://***.com/a/25305915/618464

所以,使用这个

<?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 / Invalidations / Create Invalidation),它将起作用。

【讨论】:

【参考方案12】:

这与字体无关,而是与图像有关,这可能是一种极端情况,但就像发生在我身上一样,它可能会发生在另一个人身上。我将把它留在这里,希望它对某人有所帮助:

如果您处于“我已经完成了他们所说的一切,但仍然无法正常工作”的情况,这可能是 Chrome 和 Safari 中的缓存相关问题。假设您的服务器具有正确的 CORS 配置集:

<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
    </CORSRule>
</CORSConfiguration>

在 Firefox 中一切正常,但在 Chrome 和 Safari 中却不行。 如果您从 both 一个简单的&lt;img src="http://my.remote.server.com/images/cat.png"&gt; 标记和一个 js Image 元素 src 访问您的远程图像路径,如下所示:

var myImg = new Image()
myImg.crossOrigin = 'Anonymous'
myImg.onload = () => 
  // do stuff (maybe draw the downloaded img on a canvas)

myImg.src = 'http://my.remote.server.com/images/cat.png'

您可能会在 Chrome 和 Safari 中收到 No 'Access-Control-Allow-Origin' 错误。发生这种情况是因为第一个 &lt;img&gt; 以某种方式弄乱了浏览器缓存,并且当您稍后尝试访问相同的图像时(在代码内的 Image 元素上),它就会中断。 为避免这种情况,您可以在一个 .src 路径中添加一个虚构的 GET 参数,以强制浏览器重新请求图像并避免使用缓存,如下所示:

<img src="http://my.remote.server.com/images/cat.png?nocache=true"></img>

【讨论】:

【参考方案13】:

2021 解决方案,通过在 AllowedDomains 中允许 "*" 而不冒安全风险。

步骤 1) 允许 S3 接收 CORS

S3 存储桶 > 权限 > 跨域资源共享 (CORS) 中,将您的域列表添加到 AllowedOrigins。有关示例,请参阅official doc。 AllowedMethods 只需要 GET

第 2 步)告诉 CloudFront 通过 CORS 标头发送

在您的 CloudFront 行为 origin 和 access-control-request-headers 标头发送的策略,例如Managed-CORS-S3Origin.

步骤 3) [可选,仅当您拥有多个域时]

请参阅this answer of mine,了解如何在 S3+CloudFront 的 CORS 中处理多个域。

步骤 4) 使您的 CloudFront 分配无效

祝你好运!

【讨论】:

以上是关于Amazon S3 CORS(跨域资源共享)和 Firefox 跨域字体加载的主要内容,如果未能解决你的问题,请参考以下文章

Amazon S3 CORS 仍然无法正常工作:没有“访问控制允许来源”

在 angular2 中使用 http 访问 Amazon s3

Amazon S3是否需要时间来更新CORS设置?多久?

图像保存的 con Amazon S3 存储桶未显示在 HTML5 画布上。跨域问题

跨域资源共享 (CORS) 概念

使用预签名 URL 将文件上传到 Amazon S3 时出现 CORS 错误