跨域问题解决方案汇总

全文默认讲的是浏览器端发起的 HTTP 请求的"跨域"问题(同源策略导致的受限)。

跨域 / 同源策略概述

  • **同源(same-origin)**:协议、域名(host)、端口 三者完全相同称为同源。 例如 https://example.com:443 和 http://example.com 不是同源(协议不同)。
  • **同源策略(SOP)**:浏览器的一种安全机制,限制从一个源加载的脚本去读取另一个源的响应(以防 CSRF / 数据泄露)。
  • **跨域(cross-origin)**:当请求目标不满足同源时即为跨域,请求仍可发出,但浏览器会阻止 JS 读取响应,除非服务器明确允许(即 CORS -> 跨域资源共享)。

CORS(Cross-Origin Resource Sharing)

CORS(Cross-Origin Resource Sharing)跨域资源共享。浏览器会根据响应头判断是否允许跨域读取。一些关键的响应头包括:

  • Access-Control-Allow-Origin:允许的源(或 *)。 但是这里我们一般不配置为 *,因为如果响应包含敏感数据或依赖 cookie/凭证(Authorization / session), * 与 Access-Control-Allow-Credentials: true 不能同用,浏览器也会拒绝这种组合,属于安全漏洞 ⚠️。(篇幅有限,更多细节见下篇文章。)
  • Access-Control-Allow-Methods:允许的方法(GET, POST, PUT...)。
  • Access-Control-Allow-Headers:允许的自定义 Header(如 Authorization, X-Custom-Header)。
  • Access-Control-Allow-Credentials:是否允许带 cookie/凭证(true 表示允许)。
  • Access-Control-Expose-Headers:允许前端访问的响应头。
  • Access-Control-Max-Age:预检(preflight)结果缓存时长(秒)。 预检请求(preflight):当请求使用了非"简单请求"方法或自定义了 header、或 Content-Type 非简单类型时,浏览器会先发 OPTIONS 请求询问服务器是否允许。

常见解决方案

方案 A --- 在服务端正确配置 CORS

这是最通用也最推荐的做法:​在响应里返回正确的 CORS 头​。

Express(Node)示例(使用 cors 中间件)

JavaScript 复制代码
const express = require('express')
const cors = require('cors')
const app = express()

app.use(cors({
  origin: 'https://app.example.com', // 注意不能用 '*' 配合 credentials
  methods: ['GET','POST','PUT','DELETE','OPTIONS'],
  credentials: true, // 允许 cookie
  allowedHeaders: ['Content-Type','Authorization','X-Requested-With']
}));

Nginx:把 CORS header 加到响应上

Nginx 复制代码
location /api/ {
  if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' 'https://app.example.com';
    add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE';
    add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type';
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Max-Age' 1728000;
    add_header 'Content-Length' 0;
    add_header 'Content-Type' 'text/plain charset=UTF-8';
    return 204;
  }

  proxy_pass http://backend;
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  # 把 header 添加到后端响应
  proxy_hide_header 'Access-Control-Allow-Origin';
  add_header 'Access-Control-Allow-Origin' 'https://app.example.com';
  add_header 'Access-Control-Allow-Credentials' 'true';
}

方案 B --- 前端代理到同源(仅开发阶段)

开发阶段或没有后端权限时使用我们经常使用构建工具的 dev server 实现反向代理。把跨域请求代理到本地 dev server(同源),由 dev server 转发到目标服务器。

Vite dev server 配置

JavaScript 复制代码
// vite.config.js
export default {
  server: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        rewrite: path => path.replace(/^\/api/, '')
      }
    }
  }
}

方案 C --- Nginx 反向代理(生产阶段常用)

这也是生产环境下的常用方案。在 Nginx 等反向代理层统一转发,前端调用同域(Nginx),Nginx 代理后再与后端跨域通信。

前端请求:https://app.example.com/api/...(同源) Nginx proxy_pass -> https://api.example.com/...。

Bash 复制代码
server {
    listen 80;
    server_name example.com;

    # 静态资源代理
    location /static/ {
        root /project/static;
        index index.html index.html; # 访问 /static/ 会自动"重定向"到 /project/static/index.html 文件
    }

    # API代理
    location /api/ {
        rewrite ^/api/(.*)$ /$1 break; # 如果服务器没有 /api 记得重写
        proxy_pass http://api.example.com; # 代理的地址
        
    }
}

方案 D --- JSONP(已过时,仅限 GET)

只支持 GET,通过 <script> 标签绕过 SOP,服务端返回 callback(...),容易被攻击者使用 callback 恶意函数做 XSS 攻击。

方案 E --- postMessage(跨窗口/iframe 场景)

当需要跨域页面间通信(iframe 和父窗口),使用 window.postMessage。适用于页面间数据交换,不适用于普通 API 请求。我们一般会在微前端、OAuth 第三方登录、单点登录、支付页面回调、WebView 混合开发中使用。

方案 F --- WebSocket(不受 CORS 限制)

WebSocket 握手不是标准的 CORS;如果用 WS/WSS,浏览器不会因同源限制阻止读取消息(但服务器可能做 origin 校验)。我们也不可能为了跨域强行使用 WebSocket。(之后会详细介绍 HTTP 协议和 WebSocket 协议关系和他们的使用)

开发时一些坑

在我刚开始独立从零到一搭建前后端项目的时候(当时还没有什么 AI Coding,全凭一手搜索引擎),这个问题让我红温到晚上一点也没有解决(没错,当时我还很菜 hh)。

  1. 服务端:
    1. Access-Control-Allow-Origin: https://app.example.com(具体 origin,不能是 *)
    2. Access-Control-Allow-Credentials: true
  2. 前端(fetch / xhr):
    1. fetch(url, { credentials: 'include' }) 或 xhr.withCredentials = true

​当时我犯的错误​:

  • 没有设置 credentials: 'include',浏览器不会发送 cookie。
  • 服务端回送 Access-Control-Allow-Origin: * 与 Allow-Credentials: true 同时存在(浏览器会拒绝)。
相关推荐
打工仔折腾 AI8 小时前
Spring AI + Qwen 周报生成实战:从原始日志到人工确认卡
人工智能·后端·python·spring·elasticsearch·ai agent 实战
杨文说AI与数字化8 小时前
分时预约系统的库存模型:为什么'加个预约功能'必然出现超卖与假满
后端
Darling噜啦啦8 小时前
# MySQL + Milvus = PostgreSQL?一张表搞定 AI Agent 的长时记忆(附完整代码)
后端·postgresql·orm
用户713874229008 小时前
在 .NET 中使用 AWS Secrets Manager:Config 与 Client 详解
后端
福大大架构师每日一题8 小时前
2026-10-16:交替数列的最大元素。用go语言,给定三个整数 n、s、m。现在要构造一个包含 n 个整数的序列,其中第一个数必须等于 s。序列中相邻数字的
后端
yunwei378 小时前
eBPF 入门实践教程:BPF Token,安全的委托式权限与程序加载
linux·后端·性能优化
葡萄城技术团队8 小时前
接一个前端库:以前要啃几天文档,现在三步——活字格 V12.1 AI辅助开发上手
前端
136096757238 小时前
旧 SQL 在新 PG 上能不能跑
后端
SamDeepThinking8 小时前
配货单发货数为空告警误报治理:写表延迟复核方案
java·后端·架构
知守观8 小时前
mirrorOf= 把私服请求拦去了公网镜像:本地能跑,CI 上内部包 404 排查
后端·ci/cd·maven