浏览器里按下 F12,阻止置切到 Console,跨站看到一行红字:“Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy”。点跨这不是域访报错,这是问设浏览器在替用户挡子弹。跨站请求被拦,阻止置根源在于同源策略——协议、跨站域名、点跨端口三者任一不同,域访就算跨域。问设要放行,阻止置得让服务端明确告诉浏览器:“这个来源,跨站我允许。点跨”
最直接的域访办法是加响应头。Nginx 里塞几行:
add_header 'Access-Control-Allow-Origin' 'https://your-site.com'; add_header 'Access-Control-Allow-Methods' 'GET,问设 POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization'; 注意,Access-Control-Allow-Origin 写 * 能通吃,但一旦请求带 Cookie 或 Authorization 头,浏览器就不认星号了。必须写具体域名,同时加 Access-Control-Allow-Credentials: true。我见过有人图省事写 *,结果前端带 token 的请求全挂,查了半天才揪出来。
Node.js 的 Express 项目更省事,装个 cors 中间件:
const cors = require('cors'); app.use(cors({ origin: 'https://your-site.com', credentials: true })); 但别急着 app.use(cors()) 一把梭。默认配置允许所有来源,等于把门全打开。生产环境必须收窄 origin 白名单,否则任何网站都能拿用户浏览器当跳板调你的接口。
预检请求是另一个坑。浏览器发 PUT、DELETE 或带自定义头时,会先甩一个 OPTIONS 请求探路。服务端如果没处理 OPTIONS,直接返回 405,正式请求根本发不出去。Nginx 里通常要加:
if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' 'https://your-site.com'; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization'; add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Length' 0; return 204; } Access-Control-Max-Age 设大点,省得每次请求都预检,但别超过浏览器上限(Chrome 是 2 小时)。
开发阶段更常见的场景是前端跑在 localhost:3000,后端在 localhost:8080。有人直接关浏览器安全策略,比如 Mac 上 open -n -a Google\ Chrome --args --disable-web-security。这招能跑通,但等于裸奔,任何恶意脚本都能读你的本地数据。正规做法是配 devServer 代理:
// vue.config.js 或 webpack.config.js devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } 这样浏览器以为请求发往同源,实际由开发服务器转发,绕开跨域限制。上线后前后端同域部署,或者用 Nginx 反代,问题自然消失。
JSONP 是老古董了,只支持 GET,还得后端配合返回 callback 包裹的 JS。现在除非对接上古系统,没人用它。WebSocket 不受同源策略约束,但服务端得自己校验 Origin 头,否则照样被跨站劫持。
最后提一嘴 SameSite Cookie。Chrome 80 之后默认 SameSite=Lax,跨站 POST 请求不带 Cookie。如果接口依赖 Cookie 鉴权,要么设 SameSite=None; Secure,要么改用 token 放 header。我倾向于后者,少一层浏览器策略的折腾。
跨域这事,核心就一句:浏览器拦的是响应,不是请求。服务端点头,浏览器放行。配的时候多看一眼 Network 面板里的预检请求,比翻文档快。