跨域资源共享(Cross-Origin Resource Sharing, CORS)是一种浏览器安全机制,用于控制一个源(Origin)的网页应用如何访问另一个源的资源。浏览器默认实行同源策略(Same-Origin Policy),禁止跨源请求。CORS 机制通过在服务器响应中加入特定的 HTTP 头部,授权浏览器执行跨域访问,从而为受控的跨域数据传输提供了安全的通道。
当您需要允许其他网站的页面访问您托管在 CDN 上的资源时,您需要在 CDN 的 "HTTP 响应头" 特性中进行跨域配置。
"HTTP 响应头" 特性中有一个 跨域校验 选项。该选项用于启用针对 Origin 头部的标准 CORS 校验。
Origin 在您配置的源列表中时,CDN 会在响应中返回 Access-Control-Allow-Origin 头部,其值等于请求中的 Origin。Origin 不在列表中时,响应将不包含 Access-Control-Allow-Origin 头部。Access-Control-Allow-Origin 头部为您配置的完整源列表。此方式不符合 CORS 规范,可能导致预期外的结果。以下是常见的配置场景。
如果您的资源是完全公开的,您可以允许来自任何网站的跨域请求。
Access-Control-Allow-Origin。*。* 时,响应头固定为 Access-Control-Allow-Origin: *。如果您需要允许来自单个域名的访问,请根据您网站使用的协议(HTTP 或 HTTPS)进行配置。如果您的网站同时支持这两种协议,建议将两种协议的域名都添加到允许列表中。以下示例展示了同时配置 HTTP 和 HTTPS 的情况。
Access-Control-Allow-Origin。http://www.example.com, https://www.example.com。如果您需要允许来自多个不同域名的访问,请根据每个网站使用的协议(HTTP 或 HTTPS)进行配置。以下示例展示了允许来自多个域名(例如 www.example.com 和 docs.example.com)的 HTTPS 访问。
Access-Control-Allow-Origin。https://www.example.com, https://www.example.net。如果您希望授权一个主域名下的所有子域名访问您的资源,您可以使用泛域名的方式。请根据您网站使用的协议(HTTP 或 HTTPS)进行配置。如果您的网站同时支持这两种协议,建议将两种协议的泛域名都添加到允许列表中。以下示例展示了同时配置了 http://*.example.com 和 https://*.example.com 的情况。
Access-Control-Allow-Origin。http://*.example.com, https://*.example.com。如果您在按照本文的说明配置了跨域访问后,仍然遇到请求失败的问题,可以参考以下步骤进行排查。
如果您已经按照本文的说明完成了 CDN 跨域配置,但请求仍然失败,这很可能是因为浏览器触发了预检请求(Preflight Request)。这是一种针对复杂跨域请求的安全机制,需要您在源站进行额外配置。
说明
预检请求是浏览器自动触发的一种安全机制。如果您不熟悉这个概念,或者不确定它如何影响您的跨域配置,建议您在继续操作前,先阅读 更多信息 中关于预检请求的介绍。理解这个机制可以帮助您更准确地定位问题。
由于 CDN 不会校验预检请求,您必须同时在源站上配置跨域策略来校验预检请求。以下是几个常见的源站配置示例,示例中的头部值请根据您的实际场景进行修改。
场景一:允许来自任何域名的跨域请求
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Headers: Content-Type, Authorization, X-Custom-Header Access-Control-Max-Age: 3600
场景二:允许来自特定域名的跨域请求
Access-Control-Allow-Origin: https://www.example.com Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 86400
场景三:允许跨域请求携带用户凭证
说明
根据 CORS 规范,当 Access-Control-Allow-Credentials 为 true 时,Access-Control-Allow-Origin 不能是 *。
Access-Control-Allow-Origin: https://www.example.com Access-Control-Allow-Credentials: true Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 1800
只有在收到成功的预检响应后,浏览器才会发送实际的跨域请求。
Access-Control-Allow-Origin 头部(其值应与请求中的 Origin 匹配)。Access-Control-Allow-Origin 头部或其值不匹配,浏览器将阻止页面脚本访问响应内容。说明
如果您的源站是对象存储(TOS),CDN 控制台的跨域配置(Access-Control-Allow-Origin 响应头)对您不生效。您必须在 TOS 的存储桶中独立配置 CORS 规则。具体方法,请参考 附录:为 TOS 源站配置 CORS 以处理预检请求。
如果您在完成了步骤一的排查后,跨域问题仍然存在,请检查同一用户是否会从多个不同的源(例如 http://a.example.com 和 http://b.example.com)请求同一个资源。
在这种情况下,浏览器会缓存第一个源请求到的资源。当第二个源发起请求时,浏览器会直接使用缓存,但此时资源的 Access-Control-Allow-Origin 头部可能与第二个源不匹配,导致跨域失败。
为了解决这个问题,您需要在 CDN 上额外配置 Vary: Origin 响应头。这个头部会告知浏览器,需要根据请求中的 Origin 头部来区分不同源的缓存。
注意
请勿在源站配置 Vary: Origin 响应头,原因请参见 FAQ。
Vary。Origin。当您的源站是火山引擎对象存储(TOS)时,对于非简单跨域请求,浏览器会首先发送一个预检请求(OPTIONS 请求)。CDN 无法校验预检请求,因此将该请求透传到 TOS。
如果 TOS 返回 403 Forbidden 状态码,表示预检请求未通过校验。导致校验失败的常见原因,是您未在 TOS 存储桶中配置 CORS 规则。浏览器在收到 403 响应后,会中断后续的实际请求。这就导致了即使您在 CDN 控制台为实际请求配置了正确的 Access-Control-Allow-Origin 响应头,浏览器也不会发送实际请求。
因此,您必须在 TOS 的存储桶中配置 CORS 规则,以确保 TOS 能正确校验预检请求。
您需要在存储桶管理页面的左侧导航栏中,点击 权限管理 > 跨域访问设置,然后创建一条 CORS 规则。
在 CORS 规则中,完成以下配置。
*GET, POST, PUT, DELETE(每行一个)Content-Type, Authorization, X-Custom-Header(每行一个)3600在 CORS 规则中,完成以下配置。
http://www.example.com 和 https://www.example.comGET, POST, PUT, DELETEContent-Type, Authorization, X-Custom-Header3600Q: 建议在 CDN 上配置 Access-Control-Allow-Origin 响应头,为什么不能只在源站上配置?
A: 确实,您可以在源站配置 Access-Control-Allow-Origin 头部。但在使用 CDN 的情况下,这会带来缓存管理的问题。当 CDN 缓存一个文件时,会把源站响应的 Access-Control-Allow-Origin 头部也一并缓存。如果您在源站修改了跨域策略(例如,允许了一个新的域名),CDN 上缓存的旧文件仍然带着过时的头部信息。为了让新策略生效,您必须手动刷新 CDN 上的全部相关缓存,这不仅操作繁琐,还会导致缓存命中率下降,增加回源成本。
而在 CDN 上配置此头部,CDN 会在边缘节点响应请求时,根据您的最新配置动态添加正确的 Access-Control-Allow-Origin 头部,完全避免了因缓存导致的问题,也无需手动刷新。
Q: Vary:Origin 头部有什么作用?为什么在 CDN 配置而不在源站配置?
A: 您应该在 CDN 上配置 Vary: Origin 头部,而不是在源站。原因如下:
Vary: Origin:源站会告知 CDN 需要根据请求中的 Origin 头部来区分缓存。这会导致 CDN 对同一个文件缓存多个副本(每个 Origin 对应一个)。当 CDN 收到一个来自新 Origin 的请求时,会因为找不到对应缓存而回源,这会显著降低 CDN 的缓存命中率,增加回源请求的数量和成本。Vary: Origin:CDN 只会缓存一份文件副本。所有来自不同 Origin 的请求都会命中这份缓存。CDN 在响应时,会附带上 Vary: Origin 头部,这个头部是给浏览器看的。它告知浏览器需要根据 Origin 来区分并存储响应,从而避免浏览器端的跨域缓存错误。因此,将 Vary: Origin 配置在 CDN 侧,既能确保跨域访问的正确性,又能最大化 CDN 的缓存效率。
对于一些复杂的跨域请求(即非简单请求),浏览器会先发送一个使用 OPTIONS 方法的预检请求,以确认跨域请求是否被允许。如果预检请求未通过校验,浏览器将不会发送实际的跨域请求。
常见的触发预检请求的场景包括:
PUT、DELETE、PATCH 等。Authorization、X-Custom-Header。Content-Type 是 application/json。浏览器首先发送一个 OPTIONS 方法的预检请求。这个预检请求包含了实际跨域请求将使用的关键信息:
Origin:请求的来源。Access-Control-Request-Method:实际请求将使用的方法(如 PUT、DELETE)。Access-Control-Request-Headers:实际请求将包含的自定义头部(如 Authorization)。CDN 不会校验预检请求,而是将其直接透传回您的源站。当源站收到预检请求时,必须校验请求中的 Origin、Access-Control-Request-Method 和 Access-Control-Request-Headers 头部,以判断是否允许该跨域行为。
如果允许,源站的响应需要包含以下头部:
响应头 | 说明 | 是否必需 |
|---|---|---|
Access-Control-Allow-Origin | 允许的源(域名),只能包含一个值。 | 必需 |
Access-Control-Allow-Methods | 允许跨域请求使用的 HTTP 方法。多个值以逗号(,)分隔。 | 必需 |
Access-Control-Allow-Headers | 允许跨域请求携带的自定义请求头。请求头不区分大小写,多个值以逗号(,)分隔。 | 如果预检请求包含 |
Access-Control-Max-Age | 浏览器缓存预检请求响应的时长,单位是秒。 | 可选 |
Access-Control-Allow-Credentials | 是否允许跨域请求携带用户凭据。取值为 | 可选 |
如果不允许,源站不会返回上述 Access-Control-Allow-* 头部。由于响应中未包含这些头部,浏览器会中断跨域请求,并在控制台报告错误。