本文目录导读:

在 PHP API 中处理 CORS 预检请求(OPTIONS 请求),通常需要在后端添加对应的响应头,以下是几种常见的处理方式:
简单处理方式(适用于单文件 API)
<?php
// 允许所有来源
header("Access-Control-Allow-Origin: *");
// 允许的请求方法
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
// 允许的请求头
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
// 处理预检请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
// 预检请求缓存时间(秒)
header("Access-Control-Max-Age: 86400");
http_response_code(204); // 无内容响应
exit;
}
// 您的正常 API 逻辑
// ...
?>
使用中间件方式(适用于框架)
<?php
class CorsMiddleware {
public function handle() {
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Access-Control-Max-Age: 86400");
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
}
}
// 使用
$cors = new CorsMiddleware();
$cors->handle();
?>
更灵活的处理方式(支持动态域名)
<?php
/**
* 处理 CORS 预检请求
* @param array $allowedOrigins 允许的域名列表
* @param array $allowedMethods 允许的方法
* @param array $allowedHeaders 允许的头信息
*/
function handleCors(
array $allowedOrigins = ['*'],
array $allowedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
array $allowedHeaders = ['Content-Type', 'Authorization', 'X-Requested-With']
) {
// 获取请求来源
$origin = $_SERVER['HTTP_ORIGIN'] ?? '*';
// 检查来源是否允许
if (in_array('*', $allowedOrigins) || in_array($origin, $allowedOrigins)) {
header("Access-Control-Allow-Origin: " . (in_array('*', $allowedOrigins) ? '*' : $origin));
header("Access-Control-Allow-Methods: " . implode(', ', $allowedMethods));
header("Access-Control-Allow-Headers: " . implode(', ', $allowedHeaders));
header("Access-Control-Max-Age: 86400");
// 处理凭据(如果需要)
if (in_array('*', $allowedOrigins)) {
// 如果允许所有来源,不能设置凭据
} else {
header("Access-Control-Allow-Credentials: true");
}
// 处理预检请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
}
}
// 使用示例
handleCors();
// 或者限制特定域名
// handleCors(['https://example.com', 'https://app.example.com']);
?>
完整的 API 入口文件示例
<?php
// api.php - API 入口文件
// 配置
$config = [
'allowed_origins' => ['*'],
'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
'allowed_headers' => ['Content-Type', 'Authorization', 'X-Requested-With', 'Accept'],
'max_age' => 86400 // 24小时
];
// 处理 CORS
$origin = $_SERVER['HTTP_ORIGIN'] ?? '*';
if ($origin === '*' || in_array($origin, $config['allowed_origins'])) {
header("Access-Control-Allow-Origin: " . $config['allowed_origins'][0] === '*' ? '*' : $origin);
header("Access-Control-Allow-Methods: " . implode(', ', $config['allowed_methods']));
header("Access-Control-Allow-Headers: " . implode(', ', $config['allowed_headers']));
header("Access-Control-Max-Age: " . $config['max_age']);
// 需要处理凭据时
if ($origin !== '*') {
header("Access-Control-Allow-Credentials: true");
}
// 预检请求处理
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
}
// 设置响应类型
header('Content-Type: application/json');
// API 路由逻辑
$method = $_SERVER['REQUEST_METHOD'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
switch(true) {
case $path === '/api/users' && $method === 'GET':
// 获取用户列表
echo json_encode(['users' => []]);
break;
case $path === '/api/users' && $method === 'POST':
// 创建用户
echo json_encode(['message' => 'User created']);
break;
default:
http_response_code(404);
echo json_encode(['error' => 'Not found']);
}
?>
使用 .htaccess 文件(Apache 服务器)
如果使用 Apache 服务器,也可以在 .htaccess 文件中配置:
<IfModule mod_headers.c>
# 允许所有来源
Header set Access-Control-Allow-Origin "*"
# 允许的方法
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
# 允许的请求头
Header set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
# 预检请求缓存时间
Header set Access-Control-Max-Age "86400"
</IfModule>
# 处理 OPTIONS 预检请求
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=204,L]
重要提示
-
安全性考虑:
- 生产环境不要使用
Access-Control-Allow-Origin: *,应指定具体的域名 - 限制允许的请求方法和请求头
- 生产环境不要使用
-
预检请求缓存:
- 设置
Access-Control-Max-Age可以减少预检请求次数 - 值以秒为单位,建议设为 86400(24小时)
- 设置
-
常见问题:
- 如果使用自定义请求头(如 Authorization),必须使用预检请求
- 如果前端请求包含
credentials: 'include',后端必须设置Access-Control-Allow-Credentials: true,且不能使用
-
调试建议:
- 使用浏览器的开发者工具查看 Network 请求
- 检查预检请求和实际请求的响应头
选择哪种方式取决于你的项目架构和需求,对于简单的 API,使用第一种方式就足够了;对于复杂的应用,推荐使用中间件方式进行统一管理。