HelloWorld CORS 配置指南

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

HelloWorld CORS 配置指南

HelloWorld CORS 配置指南

先用很简单的语言讲清楚 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 验证、方法/头允许、凭证策略三块来做,日常排查也会容易很多。好,差不多就是这些实用点,后面还得根据你们的具体架构再微调。

返回首页