Access-Control-Allow-Origin 头部如何工作
Access-Control-Allow-Origin 头部如何工作
技术背景
在Web开发中,由于浏览器的同源策略,出于安全考虑,会限制从脚本内部发起的跨源HTTP请求。例如,XMLHttpRequest
和 Fetch
都遵循同源策略,一个使用 XMLHttpRequest
或 Fetch
的Web应用程序只能向其自身的域名发出HTTP请求。
为了改善Web应用程序,开发者希望浏览器厂商允许跨域请求。于是,跨域资源共享(Cross-Origin Resource Sharing,CORS)机制应运而生,它为Web服务器提供了跨域访问控制,从而实现安全的跨域数据传输。现代浏览器在API容器(如 XMLHttpRequest
或 fetch
)中使用CORS来降低跨源HTTP请求的风险。
实现步骤
简单请求
当站点A尝试从站点B获取内容时,站点B可以发送一个 Access-Control-Allow-Origin
响应头,告知浏览器该页面的内容可被某些源访问。源是指域名、协议和端口号的组合。默认情况下,站点B的页面不允许任何其他源访问,使用 Access-Control-Allow-Origin
头部可以为特定的请求源打开跨源访问的大门。
对于站点B希望对站点A可访问的每个资源/页面,站点B应在响应头中提供如下信息:
1 |
|
现代浏览器不会直接阻止跨域请求。如果站点A向站点B请求一个页面,浏览器实际上会在网络层面获取请求的页面,并检查响应头是否将站点A列为允许的请求者域名。如果站点B未表明允许站点A访问该页面,浏览器将触发 XMLHttpRequest
的 error
事件,并拒绝将响应数据提供给请求的JavaScript代码。
非简单请求
如果请求是非简单请求,浏览器首先会发送一个不带数据的 “预检” OPTIONS请求,以验证服务器是否会接受该请求。满足以下任一条件(或两者都满足)的请求即为非简单请求:
- 使用除GET或POST之外的HTTP动词(如PUT、DELETE);
- 使用非简单请求头;简单请求头仅包括:
Accept
;Accept-Language
;Content-Language
;Content-Type
(仅当其值为application/x-www-form-urlencoded
、multipart/form-data
或text/plain
时为简单请求头)。
如果服务器以适当的响应头(针对非简单头的 Access-Control-Allow-Headers
,针对非简单动词的 Access-Control-Allow-Methods
)响应OPTIONS预检请求,且这些响应头与非简单动词和/或非简单头匹配,那么浏览器将发送实际请求。
例如,假设站点A要发送一个PUT请求到 /somePage
,且 Content-Type
的值为非简单的 application/json
,浏览器会先发送一个预检请求:
1 |
|
如果服务器成功响应的头信息如下:
1 |
|
那么浏览器会发送实际请求:
1 |
|
服务器会像处理简单请求一样,再次发送 Access-Control-Allow-Origin
头:
1 |
|
核心代码
Node.js + Express.js
1 |
|
PHP
1 |
|
.NET Core 3.1 API + Angular
Startup.cs
1 |
|
Controller
1 |
|
最佳实践
- 指定具体的源:尽量避免使用通配符
*
,因为这会允许任何站点与你的服务器进行交互,存在安全风险。应明确指定允许访问的源,例如Access-Control-Allow-Origin: http://siteA.com
。 - 在生产环境中避免使用浏览器扩展:虽然浏览器扩展(如Firefox的CORS Everywhere和Google Chrome的Allow CORS: Access-Control-Allow-Origin)可以在开发或测试阶段解决CORS问题,但不建议在生产环境中使用,因为它们会绕过浏览器的安全机制。
- 缓存预检请求:对于非简单请求,服务器可以设置
Access-Control-Max-Age
头,以缓存预检请求的结果,减少不必要的预检请求。
常见问题
CORS头缺失
如果出现 “CORS header ‘Access-Control-Allow-Origin’ missing” 错误,可能是服务器端未正确设置 Access-Control-Allow-Origin
头。需要确保服务器端代码中添加了该头信息。
预检请求失败
如果预检请求失败,可能是服务器未正确响应 Access-Control-Allow-Headers
或 Access-Control-Allow-Methods
头。需要检查服务器端代码,确保这些头信息正确设置。
浏览器扩展问题
在使用浏览器扩展解决CORS问题时,可能会导致生产环境中的请求仍然失败。因此,在生产环境中应使用服务器端的CORS配置。