跨域问题的由来与几种通用解法

2026-09-23 68 浏览 0 评论 0 字

跨域是浏览器同源策略下的安全机制,不是服务器在拦。本文讲清 CORS 与预检请求的原理,并给出 cros 中间件白名单、Nginx 反向代理、开发期代理和 JSONP 等多种通用解法,帮你分清开发与上线的不同处理方式,避免把 Access-Control-Allow-Origin: * 一刀切。

问题背景

前端页面在 http://localhost:8080 ,后端接口在 http://localhost:3000 ,浏览器里一调接口就报 "blocked by CORS policy"。很多人第一反应是"后端把跨域关掉",可那其实不是挂开关的事。跨域是浏览器的一个安全机制,得先弄明白它为什么存在,才知道怎么解。

原理说明

跨域(CORS,Cross-Origin Resource Sharing)是 浏览器 实现的同源策略——协议、域名、端口三者任一不同都算跨域。它的初衷是安全:防止 A 网站发起的请求去读 B 网站的敏感数据(比如你在 A 的恶意页面偷偷读取你的银行信息)。所以严格说, 不是服务器在阻拦,是浏览器在阻拦 。

判断逻辑:浏览器在请求发出前先发一个 Preflight(预检)请求 ,用 OPTIONS 方法带上 Origin ,问服务器"你允许哪些源来访问?"。服务器通过响应头回答,比如 Access-Control-Allow-Origin 。只有遇到过简单请求(GET/POST/HEAD,无自定义头)才可能跳过预检。

通用解法主要几种:

  • 后端加 CORS 响应头(最正统)
  • 后端配置 Nginx 反向代理,把前端请求转发给后端,浏览器认为同源
  • 开发阶段用代理(前端 dev server 开启 proxy)
  • JSONP(仅 GET,且已较少用)

示例代码

方案一:后端用 cors 中间件放开白名单:

// app.js
const express = require('express');
const cors = require('cors');

const app = express();

// 严格限制允许的源,别用 '*' 一刀切
const allowedOrigins = ['http://localhost:8080', 'https://www.example.com'];
app.use(cors({
  origin(origin, callback) {
    // 无 Origin(同源或非浏览器请求)直接放行
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true,   // 允许跨域携带 Cookie
}));

app.get('/api/user', (req, res) => res.json({ name: 'whoami' }));

方案二:Nginx 反代让前后端同源(推荐用于上线):

server {
  listen 80;
  server_name example.com;

  # 前端静态资源
  location / {
    proxy_pass http://frontend-app/;
  }

  # 接口转发到后端
  location /api/ {
    proxy_pass http://backend-app:3000/;
  }
}

方案三:开发期用 Vite 代理,前端代码完全不用写绝对地址:

// vite.config.js
export default {
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
      },
    },
  },
};

常见坑与解决方案

  • 坑一: credentials: true 时又写了 Access-Control-Allow-Origin: * ,浏览器会直接报错,因为带凭证不允许通配符源。解法:必须明确列出来源,不能用 * 。
  • 坑二:预检请求 OPTIONS 也走业务中间件,导致鉴权失败挡住预检。解法:让 OPTIONS 请求直接放行( app.options('*', cors()) )。
  • 坑三:本地开了代理以为跨域解决了,一上线直连后端又报跨域。解法:生产环境统一走反向代理把前后端放同源,否则就正确配置 CORS 头。
  • 坑四:自定义请求头(如把 token 放进 Authorization )触发了预检,服务器不认 Authorization 直接 404。解法: allowedHeaders 里显式允许所需请求头。

小结

跨域本质是浏览器的同源策略,解法围绕"让服务端对指定源放行"或"让前后端同源"两个方向。开发用代理、上线用反代,既干净又安全,别图省事把 * 拉满。接下来把异常返回统一格式,让接口报错也能友好。

FAQ 常见问题

Q:为什么 credentials: true 时不能配 Access-Control-Allow-Origin: * ?
A:因为带凭证(Cookie)的跨域请求不允许使用通配符源。必须显式列出允许的 Origin,否则浏览器会直接拦截报错。

Q:为什么预检 OPTIONS 请求会被鉴权挡住?
A:预检请求也会走业务中间件,若鉴权逻辑处理 OPTIONS 就会把预检拦下。解法是让 OPTIONS 请求直接放行( app.options('*', cors()) )。

Q:本地开了代理没问题,一上线直连后端又报跨域,为什么?
A:代理只在本地开发环境生效。上线后若前后端仍分属不同源,就得正确配置 CORS 响应头,或统一走反向代理让它们同源。


发布评论

发布评论前请先 登录。
0 评论
点赞
收藏

评论列表 0

暂无评论