
本教程深入探讨了Web开发中常见的跨域资源共享(CORS)问题,特别是当JavaScript通过Fetch API请求PHP后端时遇到的挑战。文章详细解释了CORS机制、预检请求(OPTIONS)的处理,并提供了PHP服务器端配置CORS响应头的完整示例代码,指导开发者如何正确配置Access-Control-Allow-Origin、Access-Control-Allow-Methods等,以确保跨域请求成功。
1. 理解跨域资源共享(CORS)
跨域资源共享(Cross-Origin Resource Sharing, CORS)是一种浏览器安全机制,旨在允许网页从不同域(协议、域名或端口)请求资源。由于浏览器的同源策略(Same-Origin Policy)限制,默认情况下,JavaScript只能与同源的服务器进行交互。当前端应用(例如运行在 http://localhost:3000)尝试向不同源的后端API(例如 https://api.example.com)发送请求时,浏览器会触发CORS机制,并要求服务器明确授权该跨域请求。如果服务器未正确配置CORS响应头,浏览器将阻止请求,并抛出“Cross-Origin Request Blocked”错误。
2. CORS请求的类型与预检请求
CORS请求主要分为两种类型:
-
简单请求 (Simple Requests):满足特定条件的GET、HEAD或POST请求。这些请求不会触发CORS预检。条件包括:
- HTTP方法是GET、HEAD或POST。
- Content-Type 仅限于 application/x-www-form-urlencoded、multipart/form-data 或 text/plain。
- 不使用自定义请求头。
- 非简单请求 (Preflighted Requests):不满足简单请求条件的请求,例如使用了PUT、DELETE方法,或POST请求的Content-Type为application/json,或使用了自定义请求头。对于非简单请求,浏览器会在发送实际请求之前,自动发送一个HTTP OPTIONS方法请求到服务器,这就是所谓的“预检请求”(Preflight Request)。预检请求的目的是询问服务器是否允许实际的跨域请求。服务器必须响应OPTIONS请求,并包含适当的CORS响应头,告知浏览器允许哪些源、方法和头。如果预检成功,浏览器才会发送实际请求;否则,请求会被阻止。
3. 关键的CORS响应头
为了正确处理CORS请求,服务器需要在响应中包含一系列Access-Control-*头:
立即学习“PHP免费学习笔记(深入)”;
- Access-Control-Allow-Origin: 必需。指定允许访问资源的来源。可以是一个具体的URL(例如 https://your-frontend-domain.com),也可以是通配符 *(表示允许任何来源,但在生产环境中应谨慎使用,因为它可能带来安全风险)。
- Access-Control-Allow-Methods: 必需。指定允许跨域访问的HTTP方法,例如 GET, POST, OPTIONS, PUT, DELETE。
- Access-Control-Allow-Headers: 必需。指定允许跨域请求携带的自定义请求头。如果请求中包含 Content-Type: application/json 或其他自定义头,服务器必须在此处列出它们。
- Access-Control-Allow-Credentials: 可选。如果前端需要发送Cookie或HTTP认证凭证(例如Authorization头),此头必须设置为 true。注意,当此头为 true 时,Access-Control-Allow-Origin 不能设置为 *,必须指定具体的来源。
- Access-Control-Max-Age: 可选。指定预检请求的结果可以被浏览器缓存多长时间(秒)。在此期间,浏览器无需再次发送OPTIONS预检请求。
4. PHP后端CORS配置实战
以下是一个PHP后端正确配置CORS以处理JavaScript跨域请求的示例。该示例着重于处理OPTIONS预检请求,并动态设置Access-Control-Allow-Origin。
true, 'message' => '数据已成功接收');
echo json_encode($response);
}
}
?>代码解释:
- Access-Control-Allow-Origin: 优先检查 $_SERVER['HTTP_ORIGIN'] 来获取请求来源。在生产环境中,强烈建议您根据一个预定义的白名单来验证 HTTP_ORIGIN,而不是无条件地回显它或使用 *。
- Access-Control-Allow-Credentials: 如果前端请求需要携带认证信息(如Cookie或Authorization头),必须将其设置为 true。同时,Access-Control-Allow-Origin 不能为 *。
- Access-Control-Max-Age: 缓存预检请求的有效时间。在此时间内,浏览器将不再发送重复的OPTIONS请求。
-
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS'): 这是处理预检请求的关键。
- 它会根据浏览器在 HTTP_ACCESS_CONTROL_REQUEST_METHOD 和 HTTP_ACCESS_CONTROL_REQUEST_HEADERS 中声明的需求,设置相应的 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers。
- 预检请求成功后,服务器应返回 204 No Content 状态码,并且必须使用 die() 终止脚本执行,以防止发送实际请求的响应内容。
- 实际请求处理: 在 OPTIONS 块之后,才是处理 GET, POST 等实际请求的逻辑。此时,CORS头已经设置完毕,可以正常返回业务数据。
5. JavaScript客户端请求示例
前端JavaScript代码通常不需要为CORS做特殊处理,只需按照常规方式发送请求即可。浏览器会自动处理预检请求和CORS验证。
const url = 'https://opecart9349.000webhostapp.com/unzipper/opencart/index.php?route=customapi/test'; // 替换为你的PHP后端URL
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json', // 触发非简单请求,需要预检
// 'Authorization': 'Bearer your_token_here' // 如果需要发送认证头,服务器也需在Allow-Headers中允许
},
body: JSON.stringify({ name: 'John Doe', age: 30 }) // 示例数据
})
.then(response => {
// 检查HTTP状态码,处理非2xx响应
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Success:', data);
})
.catch(error => {
console.error('Error:', error);
});6. 注意事项与最佳实践
- 安全性优先:永远不要在生产环境中使用 Access-Control-Allow-Origin: *,除非你明确知道所有资源都应该对所有来源开放。这可能导致跨站请求伪造(CSRF)等安全漏洞。
- 精确指定来源:始终将 Access-Control-Allow-Origin 设置为你的前端应用的具体域名(或一个域名白名单),例如 https://your-frontend-domain.com。
- 正确处理 OPTIONS 请求:这是解决CORS问题的最常见痛点。确保你的服务器能够识别并正确响应OPTIONS请求,返回正确的CORS头,并立即终止脚本。
- 使用 $_SERVER['HTTP_ORIGIN']:在动态设置 Access-Control-Allow-Origin 时,优先使用 $_SERVER['HTTP_ORIGIN'] 来获取请求来源,而不是 $_SERVER['HTTP_REFERER'],因为 HTTP_ORIGIN 是专门为CORS设计的,而 HTTP_REFERER 可能不可靠或缺失。
- 调试工具:利用浏览器开发者工具(通常是F12)的网络(Network)选项卡,检查请求和响应的HTTP头。特别关注OPTIONS请求的响应头,确保所有Access-Control-*头都正确设置。
- 服务器配置:除了代码中的CORS头,有时Web服务器(如Apache或Nginx)的配置也可能影响CORS。确保它们没有覆盖或阻止你的PHP脚本设置的CORS头。
7. 总结
跨域资源共享(CORS)是Web开发中不可避免的一部分。正确理解CORS机制,特别是预检请求的作用,并按照本文提供的PHP后端配置示例,可以有效地解决“Cross-Origin Request Blocked”错误。核心在于服务器端要明确告知浏览器,哪些跨域请求是被允许的,以及允许哪些方法、头和来源。通过细致的配置和严谨的安全考量,可以确保您的Web应用在不同域之间安全、顺畅地通信。











