跨域问题是前端开发和前后端交互过程中常见的问题,本质上是由于浏览器的同源策略的安全限制,这个机制会阻止不同源的AJAX请求,限制从一个源加载的文档(脚本)与另一个源的资源。本文将主要介绍详细跨域问题及对应的解决方案。

一、跨越问题       

        跨越问题表现

        1、使用JavaScript(如fetch,axios,XMLHttpRequest) 尝试请求一个不同源的API资源时,浏览器会拦截该请求。

        2、在浏览器的开发者工具(Console 或 Network 标签页)中,可以显示类似的错误信息:

        Access to fetch at 'https://api.other-site.com/data' from origin 'https://your-site.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

        Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at...

        3、请求在标签页中可能显示为失败(红色)或带有警告图标,状态码可能为200(实际服务器已响应,但浏览器因 CORS 头缺失或无效而阻止了响应数据)

        实际场景:

        1、本地开发环境:需要同时运行前端和后端时,前端运行在 http://localhost:3000(如 React/Vue 开发服务器),后端 API 部署在 http://localhost:8080 或远程测试服务器 https://api-test.com。由于端口不同,或者域名不同,当前端调用 API 时,浏览器控制台报错。。

        2、前端直接调用第三方 API:请求会被浏览器拦截。
 

        同源策略

        同源策略是浏览器限制一个源(Origin)的脚本或文档与另一个源的资源进行交互(包括:访问API接口、访问iframe内容等)的一种安全机制。

        同源判断包括:两个 URL 的协议 (Protocol)、域名 (Hostname)、端口 (Port) 三者必须完全相同才被视为同源。例如:

        https://www.example.com:443/app 与 https://www.example.com:443/api 视为同源(协议、域名、端口相同)

        https://www.example.com/app 与 http://www.example.com/api 不同源(协议不同)

        https://www.example.com/app 与 https://api.example.com/data 不同源(子域名不同)

        https://www.example.com:443/app 与 https://www.example.com:8080/api 不同源(端口不同)

二、解决方案

        1、CORS (跨域资源共享) 

        依据 W3C 标准,在目标服务器的 HTTP 响应头中添加特定的 CORS 相关头部,明确告知浏览器哪些源 (Origin)、方法 (Methods)、头部 (Headers) 是被允许访问的,以及是否允许发送凭据 (Credentials)。

        实现方式:

        (1)手动设置响应头: 在服务器端代码(如 Node.js/Express, Python/Django/Flask, Java/Spring, PHP, .NET)中,在处理请求的代码逻辑里添加相关响应头,明确可以访问的内容。具体响应头如下:

响应头 参数值
Access-Control-Allow-Origin (必需)

1、值可以是具体的请求来源(如 https://your-site.com),表示允许该来源访问

2、也可以是*,表示允许任何来源访问(注意:使用 * 时,浏览器会忽略 Access-Control-Allow-Credentials: true 和 Cookie 等凭据信息,不适用于需要预检的请求携带自定义头的情况)

Access-Control-Allow-Methods (预检请求必需) 允许客户端使用的 HTTP 方法(如 GET, POST, PUT, DELETE, OPTIONS)
Access-Control-Allow-Headers (预检请求必需) 客户端请求中允许携带的非简单请求头(如 Content-Type, Authorization, X-Custom-Header)
Access-Control-Allow-Credentials (可选) 值为 true 时,表示允许浏览器在跨域请求中包含凭据(如 Cookies, HTTP认证)
Access-Control-Max-Age (可选) 指定预检请求(OPTIONS)结果可以被缓存多久(秒),减少后续请求的预检次数

        (2)使用中间件/库: 现有 Web 框架有成熟的 CORS 中间件/库(如 Express 的 cors,Django 的 django-cors-headers,Spring Boot 的 @CrossOrigin 注解),配置较为方便。简单的配置示例如下:

const express = require('express');
const cors = require('cors'); // 使用 cors 中间件

const app = express();

// 简单配置:允许所有来源访问所有路由
// app.use(cors()); // 相当于 Access-Control-Allow-Origin: *

// 更精细的配置
const corsOptions = {
  origin: 'https://your-client-site.com', // 或一个函数动态判断
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true, // 允许发送凭据 (cookies)
};
app.use(cors(corsOptions));

// ... 你的路由定义
app.listen(3000);
        优缺点:

        优点: 标准化、安全可控(由服务器决定权限)、功能完整(支持各种方法、头、凭据)。

        缺点: 需要修改服务器端代码。

        2、JSONP (JSON with Padding) -现已不常用

        实现方式:

        利用 <script> 标签不受同源策略限制的特性。客户端动态创建一个 <script> 标签,src 指向目标 API URL(通常需要服务器支持 JSONP 回调),并传递一个回调函数名作为参数(如 ?callback=myCallback)。服务器将数据包裹在这个回调函数调用中返回(如 myCallback({"data": "value"});)。浏览器加载脚本时,会自动执行回调函数,客户端在回调函数中处理数据。适用于 GET 请求

        优缺点:

        优点: 兼容性极好(支持非常老的浏览器)。

        缺点:仅支持 GET 请求;安全性差: 难以进行错误处理(只能超时),存在注入攻击风险(服务器返回恶意脚本会被执行);需要服务器端特殊支持。

        适用于对兼容性要求极高且仅需 GET 数据的简单场景,基本已经被CORS替代

        3、代理服务器 (Proxy Server) 

        同源策略仅是浏览器的限制,而服务器之间通信不受此限制。因此,客户端可以不直接请求目标 API,而是请求一个同源的代理服务器。之后代理服务器将请求转发给真正的目标 API,获取响应后,再返回给客户端。对于客户端浏览器而言,始终是在和同源的代理服务器通信。从而解决跨域问题。

        实现方式:

        1、针对开发环境:使用前端开发工具内置的代理功能(如 webpack-dev-server 的 proxy 配置,Vite 的 server.proxy),配置较为简单,仅在开发过程中生效。示例如下:

module.exports = {
  // ...其他配置
  devServer: {
    proxy: {
      '/api': { // 将所有以 /api 开头的请求代理到 target
        target: 'https://api.other-site.com',
        changeOrigin: true, // 修改请求头中的 Host 为目标地址的 host
        pathRewrite: { '^/api': '' }, // 可选:重写路径(去掉 /api 前缀)
      }
    }
  }
};

        2、针对生产环境:可以使用Nginx / Apache 反向代理: 在部署前端应用的 Web 服务器(如 Nginx)上配置反向代理规则。示例如下:

server {
    listen 80;
    server_name your-site.com;

    location /api/ { # 将所有 /api/ 路径的请求代理到目标 API
        proxy_pass https://api.other-site.com/; # 目标 API 基础地址
        proxy_set_header Host $proxy_host; # 可选:设置正确的 Host 头
        proxy_set_header X-Real-IP $remote_addr; # 传递客户端 IP
        # 可以添加其他需要的头部...
    }

    location / {
        root /path/to/your/frontend/dist; # 前端静态文件目录
        try_files $uri $uri/ /index.html; # 处理 SPA 路由
    }
}
        优缺点:

        优点:客户端代码无需任何特殊处理;完全规避浏览器的 CORS 限制;可以隐藏真实 API 地址,增加安全性(对外只暴露代理地址);方便统一添加认证、日志、缓存等逻辑到代理层。

        缺点:增加了架构复杂度(需要部署和维护代理服务器);需要额外的服务器资源;会增加请求延迟(多了一次网络跳转)。

        4、 WebSocket

         WebSocket 协议 (ws://, wss://) 本身不受同源策略限制。浏览器在建立 WebSocket 连接时,会发送一个 Origin 头。服务器可以选择是否基于这个 Origin 来接受或拒绝连接。 一旦连接建立成功,客户端和服务器就可以进行全双工的通信,从而解决跨域问题。

        实现方式:

        需要使用 WebSocket 客户端库(浏览器原生支持 WebSocket 对象)和 WebSocket 服务器库(如 Node.js 的 ws, Socket.IO)

        适用场景: 需要双向实时通信的应用(聊天室、实时数据推送、在线游戏)。不适用于普通的 HTTP API 调用。

        5、其他方案

        (1)document.domain (降域): 仅适用于主域相同,子域不同的情况(如 a.example.com 和 b.example.com)。双方页面设置 document.domain = 'example.com'。限制较多且不安全,不推荐使用。

        (2)window.postMessage: 主要用于不同源窗口/iframe 之间的安全通信(传递字符串数据)。需要双方页面协作监听 message 事件并验证来源。不适用于直接调用 API。

        以及修改浏览器设置 / 禁用 CORS 等方式。

        当遇到跨域问题时,应首选CORS(目标API服务器可以控制),方案最标准、安全,功能完备,仅需配置服务器端响应头即可。无法修改目标API的情况下,可以使用使用反向代理 (Nginx/Apache)的方式。

附注:

1、简单请求 (Simple Request):

方法包括:GET, HEAD, POST,请求头仅限于 Accept, Accept-Language, Content-Language, Content-Type (且 Content-Type 的值仅限于 application/x-www-form-urlencoded, multipart/form-data, text/plain)

流程:浏览器直接发送实际请求(GET/POST等),服务器响应中必须包含 Access-Control-Allow-Origin 头(可能还需要 Access-Control-Allow-Credentials)。

2、预检请求 (Preflight Request):

不符合简单请求条件的请求(如使用了 PUT, DELETE, PATCH 方法,或 Content-Type: application/json,或包含自定义请求头)。

流程:a.浏览器先自动发送一个 OPTIONS 方法的预检请求到目标服务器。

b.预检请求携带 Origin, Access-Control-Request-Method(实际请求的方法),Access-Control-Request-Headers(实际请求的自定义头)。

c.服务器检查预检请求头,响应须包含有效的 Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers(根据请求头确定)等。

d.浏览器收到预检响应,检查是否允许实际请求。如果允许,则接着发送实际的请求(GET/POST/PUT等)。如果不允许,则阻止实际请求并报错。  

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐