摘要:网站页面从自己的域名向阿里云 OSS 或腾讯云 COS 直传、读文件时,浏览器可能先发 OPTIONS 预检。本文按阿里云、腾讯云和 MDN 当前文档,核对来源、允许方法、允许请求头、暴露响应头和 MaxAge,并对照控制台报错。图床与防盗链见前文。查询日期 2026-11-08。只放官网普通链接(affiliate_later)。

依据:阿里云《配置CORS规则允许网站跨域访问OSS资源》、阿里云《Options跨域预检请求》、阿里云《处理因未开启跨域设置导致Options请求报错0003-00000601》、腾讯云《设置跨域访问》、MDN《跨源资源共享(CORS)》。查询日期:2026-11-08(北京时间)。只放官网普通链接(affiliate_later: 1)。配置以你当前控制台和官方当前页为准。不构成运维建议;文中出现的字段名、示例秒数和报错代码均来自上述页面,未在官方页看到的状态码或 TTL 不另写。

网页在 https://www.example.com 上打开,上传或读取却指向 OSS、COS 的另一个域名时,浏览器控制台常出现 blocked by CORS policy,文件没发出去,或预检 OPTIONS 直接失败。把对象存储当图床、配防盗链 Referer、防流量被刷,见前文对象存储做图床:阿里云 OSS / 腾讯云 COS。图床与防盗链见前文,本文只谈跨域预检与上传失败。 整站备份和生命周期另见对象存储整站备份。

1. 浏览器为什么会先发 OPTIONS

MDN 把跨源资源共享(CORS)写成一套 HTTP 头机制:服务器用响应头标明,除了它自己以外,哪些源(协议、域名或端口)可以通过浏览器访问这些资源。为了确认服务器是否允许接下来的真实请求,浏览器会先对跨源资源发一次「预检」请求;预检里带上真实请求将使用的 HTTP 方法和头部。

MDN 还写:CORS 失败时 JavaScript 往往拿不到具体原因,只能到浏览器控制台里看报错。所以排上传失败,优先打开开发者工具的 Network 和 Console,看有没有一条方法为 OPTIONS 的请求,以及它的响应头。

阿里云《跨域设置》把跨域请求分成两类,和预检是否发出有关:

类型该页写的触发条件需要核对的 CORS 配置
简单请求同时满足:方法是 GET / HEAD / POST;若是 POST,Content-Type 为 text/plain、application/x-www-form-urlencoded 或 multipart/form-data;没有自定义头部来源、允许 Methods
预检请求满足任一:方法不是 GET / HEAD / POST(例如 PUT / DELETE);POST 的 Content-Type 是其他值;带了自定义头部(例如 x-oss-*)来源、允许 Methods、允许 Headers

前端直传到对象存储,常见情况是用 PUT 或带 x-oss-*、Authorization 这类头部,正好落在预检这一类。阿里云对预检流程的写法是:

  1. 浏览器先发 OPTIONS,并带上 Access-Control-Request-Method(真实请求的方法)和 Access-Control-Request-Headers(真实请求会用到的头部)。
  2. OSS 按 Bucket 的 CORS 规则检查方法和头部是否被允许。预检里任何一个方法或头部不在允许集合中,请求失败,并且不会再发后面的真实请求。
  3. 预检通过后,真实请求再走和简单请求相同的核对:响应里要有匹配的 Access-Control-Allow-Origin。

阿里云《Options跨域预检请求》补充了请求头名称:Origin 标识来源域,实际请求里只能设一个;Access-Control-Request-Method 同样只能设一个;Access-Control-Request-Headers 可以有多个,之间用英文逗号分隔。对应的响应头包括 Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers、Access-Control-Expose-Headers 和 Access-Control-Max-Age(该页示例里 Max-Age 单位为秒)。若来源、方法或头部不被允许,响应里就不会带上相应的允许头。

对象存储跨域 CORS:上传失败与浏览器预检怎么核对(2026)-有序
阿里云帮助中心《跨域设置》:网站加载 OSS 资源时浏览器可能报 blocked by CORS policy;同源要求协议、域名和端口都相同(2026-11-08 截自官方文档)

2. 控制台要核对的字段

两家控制台的入口和字段名略有差别,先对上你正在用的那一家,再按文档逐项勾。

阿里云 OSS:Bucket 列表打开目标 Bucket,左侧选 数据安全 > 跨域设置,点创建规则。文档列出的参数是:

控制台字段文档怎么写核对时看什么
来源必填。格式为 协议://域名或IP地址[:端口],例如 https://www.example.com 或 http://192.168.1.1:8080。支持 *,但每个来源里最多只能用一个。有效示例:https://*.example.com、http://localhost:*;无效示例:https://*.example.*、https://*。可写多个来源,每行一个是否漏了 https://,是否和浏览器地址栏里的协议、域名、端口完全一致
允许 Methods必填。可选 GET、PUT、POST、DELETE、HEAD,可多选直传上传有没有勾上 PUT 或 POST
允许 Headers非必填,作用于预检。支持 * 表示允许所有头部;可多行,大小写不敏感预检里的 Access-Control-Request-Headers 是否都能对上,例如 Authorization、content-type、x-oss-*
暴露 Headers非必填。允许前端 JS 读取哪些响应头;不支持 *。若要在 JS 里取 ETag 或 x-oss-request-id,必须写上上传后读不到 ETag 时,先看这里有没有写
缓存时间(秒)非必填。浏览器缓存这次 OPTIONS 结果的时间;缓存有效期内,同一资源的相同跨域请求不再发预检改完规则仍像「没生效」时,可能是旧预检还在缓存
返回 Vary: Origin非必填。告知 CDN 等中间缓存按请求的 Origin 区分版本。文档写:来源配置了多个域名或通配符时必须开启;开启后可能导致 CDN 缓存命中率下降多个来源互相「串许可」时优先看这一项

同一页还写:OSS 按规则从上到下检查,使用第一条匹配成功的规则,后面的不再看。每个 Bucket 最多可以配置 20 条跨域规则。前端直传场景的示例里,允许 Methods 写的是 PUT、POST,暴露 Headers 写的是 ETag、x-oss-request-id。

安全方面,该页建议:除非 Bucket 完全公开,否则不建议将来源设成 *,应写明网站域名;只开放业务需要的方法;允许 Headers 尽量逐项列出,避免使用 *。这些是文档里的最佳实践表述,具体取值仍以你当前业务和控制台为准。

对象存储跨域 CORS:上传失败与浏览器预检怎么核对(2026)-有序
阿里云帮助中心《跨域设置》:数据安全 > 跨域设置,来源、允许 Methods、允许 Headers、暴露 Headers、缓存时间与返回 Vary: Origin(2026-11-08 截自官方文档)

腾讯云 COS:存储桶详情左侧选 安全管理 > 跨域访问 CORS 设置,点添加规则。文档对字段的说明是:

  • 来源 Origin(必填):添加自己使用的域名,支持域名(包括中文域名)和 IP。域名末尾不需要带 /;可同时指定多个来源,每行一个。支持 * 表示全部允许,文档写不推荐。若 AJAX 开启了 xhr.withCredentials = true,该场景不允许配置 Access-Control-Allow-Origin:*,需要写具体域名;文档同时说明 COS 域名一般用于静态网站、通常不必开 withCredentials。支持 http://www.example.com 这种具体域名,也支持 http://*.example.com 这种二级泛域名,但每行只能有一个 *。不要漏掉 http 或 https;端口不是默认 80 时还要带上端口。中文域名建议同时添加中文域名和 punycode 编码后的域名。
  • 操作 Methods:支持 GET、PUT、POST、DELETE、HEAD,可枚举一个或多个。
  • Allow-Headers:在发送 OPTIONS 时告知服务端,接下来的请求可以使用哪些自定义 HTTP 请求头,例如 x-cos-meta-md5。可多行、每行一个;文档写 Header 容易遗漏,没有特殊需求时建议设为 *。Access-Control-Request-Headers 里出现的每个 Header,都必须在 Allowed-Headers 里有对应项。
  • Expose-Headers:返回 COS 常用 Header,需按应用需求填写,默认推荐填写 ETag。不允许使用通配符,大小写不敏感,支持多行且每行一个。
  • 超时 Max-Age:OPTIONS 请求结果的有效期(秒),必须是正整数,文档举例为 600。
  • 返回 Vary: Origin:浏览器同时存在 CORS 和非 CORS 请求时请启用,否则会出现跨域问题。文档提醒:启用后可能会造成浏览器缓存或 CDN 回源增加;该选项是全局配置,修改后所有跨域规则的该字段会一起更新。

腾讯云这一页还有一句容易忽略的说明:COS 会按你设定的规则响应 OPTIONS,但服务端并不会校验随后发起的跨域请求是否符合规则。浏览器仍会检查响应头;规则写宽了,后续真实请求不会被 COS 再拦一层。

对象存储跨域 CORS:上传失败与浏览器预检怎么核对(2026)-有序
腾讯云《设置跨域访问》:存储桶安全管理里的跨域访问 CORS 设置,来源、Methods、Allow-Headers、Expose-Headers 与 Max-Age(2026-11-08 截自官方文档)
对象存储跨域 CORS:上传失败与浏览器预检怎么核对(2026)-有序
MDN《跨源资源共享(CORS)》:服务器用 HTTP 头标明允许的源,浏览器会先对跨源资源发起预检请求(2026-11-08 截自 MDN)

3. 上传失败时怎么对照

先在浏览器 Network 里找到那条 OPTIONS,看请求头里的 Origin、Access-Control-Request-Method、Access-Control-Request-Headers,再回到控制台规则逐项对比。官方页里已经写明、可以直接用来对照的常见情况如下。

控制台还没开 CORS,或规则匹配不上

阿里云《0003-00000601》写:对某个 Bucket 发了 Options 请求,但目标 Bucket 没有开启跨域配置,就会出现该错误。对应的《Options》接口文档把这种情况记为错误码 AccessForbidden,HTTP 状态码 403:未开启 CORS,或预检按规则评估后不被允许,都会返回这个错误码。处理入口是先到「数据安全 > 跨域设置」补规则,再重试预检。

来源没有放行当前页面

来源必须带协议,并和地址栏一致。https://www.example.com 和 http://www.example.com 在阿里云的跨域表里是不同源;端口不同也算跨域。只写了 www.example.com、漏了 https://,或生产站是 https://app.example.com 却只放行了 https://www.example.com,预检都会对不上。

方法里漏了 PUT 或 POST

阿里云「前端直传文件」示例把允许 Methods 写成 PUT、POST,并说明这是上传操作需要的方法。只勾了 GET 和 HEAD,页面可以读对象,浏览器一发上传预检就会失败,真实的 PUT/POST 不会发出。

允许 Headers 没有覆盖预检里的头部

预检里若带了 authorization、content-type、x-oss-* 或 x-cos-meta-md5,这些名字都要能在「允许 Headers / Allow-Headers」里找到对应项。腾讯云写得很明确:Access-Control-Request-Headers 中的每个 Header,都必须在 Allowed-Headers 中有对应项。阿里云则说明:预检中任何头部不在允许集合里,请求失败,真实请求不会发送。

预检或响应头被 CDN 改写、拦下

阿里云写:如果 Bucket 开启了 CDN 加速并使用 CDN 域名访问,跨域请求会先到 CDN 节点。两种配法是:在 CDN 控制台配置跨域规则,由 CDN 直接返回 CORS 头;或让 CDN 透传源站 CORS 头,此时需要在 OSS 侧配置规则。文档强调:OSS 侧的 CORS 配置,仅在请求直接访问 OSS 源站域名,或 CDN 被配成透传源站响应头时生效。FAQ 还写:若 curl 看到两个跨域头或与配置不符,可能是使用了 CDN 加速 OSS。开了 CDN 或前面还有 WAF 时,只改 Bucket 上的 CORS,浏览器看到的头不一定是你刚保存的那一份。

浏览器或中间缓存还记着旧响应

阿里云 FAQ 对 No 'Access-Control-Allow-Origin' header is present on the requested resource 的说明是:常见原因是浏览器缓存了不带 CORS 头的旧响应,或规则匹配不上。该页给出的排查顺序是:清除浏览器缓存后重试;检查来源和允许 Methods 是否与实际请求匹配;必要时临时用一条更宽松的规则做定位(来源 *、方法全选、允许 Headers 为 *、暴露 ETag 和 x-oss-request-id、缓存时间设为 0、勾选返回 Vary: Origin);仍不行时在服务器用 curl -v -H 'Origin: <来源地址>' '<OSS资源链接>' 查看实际响应头。

若响应里已经有 Access-Control-Allow-Origin,但其值与当前请求的 Origin 不一致,该页归因于多来源场景下的缓存:浏览器或 CDN 可能记住了给另一个网站的许可。处理方式是在规则里开启返回 Vary: Origin,或清除浏览器缓存后再访问。

带凭证的请求还有一条限制:前端 credentials 为 include、或 xhr.withCredentials = true 时,来源不能是通配符 *。阿里云和腾讯云、MDN 都写了这一点。需要带 Cookie 或认证信息时,来源写成具体域名;不需要带凭证时,把前端的 withCredentials 关掉,并确认服务端不要返回互相冲突的凭证头。

对象存储做图床时的 Referer 防盗链、空 Referer 和防盗刷,仍然按前文图床与防盗链核对,不要和 CORS 来源写成同一组规则。WordPress 伪静态导致的 404,见固定链接与 Nginx rewrite,和跨域预检要分开排查。

核对清单(可勾)

  1. □ 浏览器 Network 里能看到 OPTIONS,并记下 Origin、Access-Control-Request-Method、Access-Control-Request-Headers
  2. □ OSS 打开的是「数据安全 > 跨域设置」,COS 打开的是「安全管理 > 跨域访问 CORS 设置」
  3. □ 来源带协议,和当前页面的协议、域名、端口一致;多个来源或通配符时已考虑 Vary: Origin
  4. □ 允许 Methods 覆盖实际上传或读取用的方法,直传上传有 PUT 或 POST
  5. □ 允许 Headers 覆盖预检里出现的每一个自定义头
  6. □ 前端需要读取 ETag 或 x-oss-request-id 时,已写进暴露 Headers(OSS 不支持在这里用 *)
  7. □ 走 CDN 域名访问时,CORS 头是在 CDN 配的,还是透传源站,已经对上
  8. □ 改完规则后清过浏览器缓存,或把预检缓存时间临时调低再试

相关阅读

官网入口(普通链接,无返利):阿里云 OSS 跨域设置、阿里云 Options 预检、阿里云 0003-00000601、腾讯云设置跨域访问、MDN CORS。