HelloWorld CORS 配置指南
要让 HelloWorld 服务在浏览器中安全可访问,关键是服务端正确返回 CORS 响应头并妥善处理预检(OPTIONS)请求:识别并允许特定 Origin、声明允许的方法与自定义头、决定是否允许带凭证(cookie、认证头),并在允许凭证时避免使用通配符 Origin。生产环境应以白名单为主、限制暴露头、设置合理的预检缓存并配合 CSRF 或反向代理等防护手段。


Table of Contents
Toggle先用很简单的语言讲清楚 CORS 在做什么
想象浏览器是一个门卫,网页脚本像访客想从别的房子取东西。出于安全,门卫会先问“你从哪个房子来?”(Origin),然后目标服务器要在回信里写清楚是否允许访问。如果回信里没有允许信息,门卫就会阻止脚本访问响应内容。CORS 就是这套问答与规则集合。
CORS 的核心要素(把复杂拆成小块)
- Origin 检查:浏览器总会带一个 Origin 头告诉服务器请求来自哪个源(协议+域名+端口)。
- 响应头决定允许与否:常见头包括 Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers、Access-Control-Allow-Credentials、Access-Control-Max-Age、Access-Control-Expose-Headers。
- 简单请求 vs 复杂请求:简单请求(如 GET/POST 且 Content-Type 是标准三类)不会触发预检;复杂请求会先发一个 OPTIONS 预检请求,确认允许后再正式发实际请求。
- 凭证(cookies / Authorization):如果要带凭证,浏览器会要求 response 中 Access-Control-Allow-Credentials 为 true,且此时 Access-Control-Allow-Origin 不能是星号(*)。
简单请求和复杂请求的区别,别搞混
简单请求满足三个条件:方法是 GET/POST/HEAD,Content-Type 是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain,且没有自定义头。否则就是复杂请求,会先发 OPTIONS 预检。
浏览器与服务器间的典型交互流程
- 普通(简单)请求:浏览器发送请求(含 Origin),服务器返回响应并在响应头里加上允许信息,浏览器决定是否把响应交给脚本。
- 复杂请求:浏览器先发 OPTIONS(含 Origin、Access-Control-Request-Method、Access-Control-Request-Headers),服务器用 Access-Control-Allow-* 系列头回应是否允许,若允许浏览器再发实际请求。
关键响应头速查表
| Header | 作用 |
| Access-Control-Allow-Origin | 允许的来源,单个域或 *(注意凭证限制) |
| Access-Control-Allow-Methods | OPTIONS 预检回应允许的 HTTP 方法 |
| Access-Control-Allow-Headers | 预检回应允许的自定义请求头 |
| Access-Control-Allow-Credentials | 是否允许携带凭证(true/false) |
| Access-Control-Max-Age | 预检结果在浏览器的缓存时间(秒) |
| Access-Control-Expose-Headers | 允许前端读取的响应头列表 |
常见服务器配置示例(实战模板)
下面给出常见后端或代理的最小可用配置片段,实际部署时请根据业务白名单和安全策略调整。
Node.js + Express(推荐使用中间件,示例自行精简)
使用 cors 包:
const express = require('express');
const cors = require('cors');
const app = express();
const whitelist = ['https://app.example.com'];
app.use(cors({
origin: function(origin, cb){
if(!origin) return cb(null, false); // 非浏览器请求视需求处理
if(whitelist.indexOf(origin) !== -1) return cb(null, true);
cb(new Error('Not allowed by CORS'));
},
credentials: true,
methods: ['GET','POST','PUT','DELETE','OPTIONS'],
allowedHeaders: ['Content-Type','Authorization','X-Requested-With']
}));
Nginx(反向代理方式)
在 server 或 location 中加入:
add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
# 对于预检请求,可以快速返回 204
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 3600;
return 204;
}
Apache(.htaccess 或虚拟主机配置)
Header always set Access-Control-Allow-Origin "https://app.example.com" Header always set Access-Control-Allow-Methods "GET,POST,OPTIONS" Header always set Access-Control-Allow-Headers "Content-Type,Authorization" Header always set Access-Control-Allow-Credentials "true"
Spring Boot(Java)
全局配置示例:
import org.springframework.web.servlet.config.annotation.*;@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/") .allowedOrigins("https://app.example.com") .allowedMethods("GET","POST","PUT","DELETE","OPTIONS") .allowedHeaders("Content-Type","Authorization") .allowCredentials(true) .maxAge(3600); } }
Flask(Python)
from flask import Flask from flask_cors import CORS app = Flask(__name__) CORS(app, origins=['https://app.example.com'], supports_credentials=True)
Django(Python)
使用 django-cors-headers:
# settings.py CORS_ALLOWED_ORIGINS = ['https://app.example.com'] CORS_ALLOW_CREDENTIALS = True
凭证和通配符的那点坑
很多人直接把 Access-Control-Allow-Origin 设为 * 然后又希望带 cookie,这在浏览器里不允许:当 Access-Control-Allow-Credentials 为 true 时,Access-Control-Allow-Origin 不能是星号。解决办法是按 Origin 返回具体域名(动态设置)。
预检缓存与性能考量
Access-Control-Max-Age 可以减少预检次数,但不要盲目设太长,尤其在安全策略或授权频繁变化的场景下。不同浏览器对最大缓存时间的支持有差异,常用值为 600-86400 秒。
常见故障与排查清单(像做实验那样一步步排)
- 浏览器控制台报跨域错误:查看请求/响应的 Origin 与 Access-Control-Allow-Origin 是否匹配。
- 预检失败:检查服务器是否对 OPTIONS 请求返回正确的允许头,并返回 200/204 状态。
- 带凭证仍然失败:确认前端 fetch 或 XHR 设置了 credentials: ‘include’,并且服务器设置了 Access-Control-Allow-Credentials: true 与具体 Origin。
- 响应头看不到自定义头:需要在 Access-Control-Expose-Headers 列出允许前端读取的头。
- 重定向导致问题:如果跨域请求发生重定向,最终响应的 Origin 与最初不同,浏览器可能阻止,应避免跨域重定向或在最终目标返回正确的 CORS 头。
关于安全:不要把 CORS 当做访问控制
CORS 是浏览器的一道客户端保护门禁,而不是服务器的身份验证或授权。服务器仍需验证请求是否合法(基于 token、session、权限等)。将 CORS 配置得太宽相当于把门卫睡着了,配合 CSRF token、SameSite cookie、反向代理和合理的白名单策略才算稳妥。
进阶技巧与常见场景
- 动态返回 Origin:对于多个子域,服务器可以检查请求中的 Origin 是否在白名单内,然后把该 Origin 写回到 Access-Control-Allow-Origin。
- 只在必要接口启用 CORS:不要全站放行,按接口分级启用可以减少风险。
- 使用反向代理:把跨域请求通过同域代理转发给 API 服务,前端无需 CORS 控制,这在既想安全又想简化跨域时很实用。
- 调试技巧:用 curl 或 Postman 请求不会触发浏览器 CORS 行为,排查时要同时看浏览器的 Network 面板和服务器端日志。
小结前的最后几句话(像边想边写)
做 CORS 时,思路总是先清楚业务是否需要跨域、是否需要携带凭证,然后把白名单、预检和缓存策略理清楚。配置看似繁琐,但其实把规则拆成 Origin 验证、方法/头允许、凭证策略三块来做,日常排查也会容易很多。好,差不多就是这些实用点,后面还得根据你们的具体架构再微调。