服务器CORS策略允许PATCH,为何我的PATCH请求仍被拦截?
根据Gist API的说明,它明确允许所有*主机和PATCH方法:
access-control-allow-origin: * access-control-allow-methods: GET,PUT,PATCH,OPTIONS,POST
但当我在运行于http://localhost的开发应用中,通过浏览器fetch API向私有Gist发送带有有效令牌的PATCH请求时,Chrome却将其拦截,提示:
从源
'http://localhost'获取'https://api.github.com/gists/c4c...86d'的请求已被CORS策略阻止:预检响应中的Access-Control-Allow-Methods不允许使用patch方法。
- 我已尝试使用非localhost地址,如
http://127.0.0.1、http://local.host等。 - 据我理解,由于允许源设置为
*,HTTP与HTTPS跨域也不应成为问题。
有趣的是,预检请求出现在被拦截的PATCH请求之后:


- 明明服务器允许PATCH,为何Chrome仍声称该方法不被允许?
- 为何Chrome在我的PATCH请求之后才发送预检请求?
问题1解答
核心原因是私有Gist的预检OPTIONS请求未携带认证令牌,导致GitHub返回的CORS头不包含PATCH方法。
Gist API文档标注的CORS配置是针对公开资源的,当请求私有Gist时,GitHub要求请求必须携带认证信息。但根据CORS规范,浏览器发送预检OPTIONS请求时,默认不会携带Authorization令牌。此时GitHub对未认证的OPTIONS请求返回的Access-Control-Allow-Methods头,仅包含GET、OPTIONS等基础方法,不会包含PATCH,因此Chrome判定该方法不被允许。
另外存在极小概率的大小写问题:如果请求中使用了小写patch方法名,而GitHub返回的头中是大写PATCH,不过现代浏览器一般会忽略方法名的大小写,这个可能性极低。
问题2解答
你看到的“PATCH请求在前,预检在后”是Chrome网络面板的视觉误导,实际流程为:
- 浏览器尝试发送PATCH请求,但检测到这是跨域且需要预检的请求(PATCH属于非简单方法,同时请求携带了认证令牌),因此立即取消了该PATCH请求。
- 随后浏览器发送正式的OPTIONS预检请求。
在Chrome网络面板中,被取消的PATCH请求会保留在列表中,后续的OPTIONS请求会排在它后面,看起来像是PATCH先发送。你可以检查PATCH请求的状态,应该显示为“已取消(Cancelled)”,以此验证这个流程。如果开启面板的“保留日志(Preserve log)”选项,就能清晰看到完整顺序:OPTIONS先发送,预检通过后才会发送PATCH请求。
内容的提问来源于stack exchange,提问作者Marc

