在网站开发中,前端页面与后端服务并不总是部署在同一个域名下。尤其是在前后端分离、微服务架构、调用第三方 API 的场景中,不同域名之间的 HTTP 请求变得十分常见。然而,很多开发者在调试时都会遇到这样的问题:请求发出去后,浏览器控制台报出 “No ‘Access-Control-Allow-Origin’ header” 或 “跨域访问被阻止” 的错误。这就是典型的跨域访问被拦截问题。
跨域访问的限制,源自浏览器的同源策略。为了保护用户数据和防止恶意脚本,浏览器默认禁止网页向不同源的服务器发送或读取非安全的请求响应。虽然这种限制对安全性有益,但在合法的业务场景中,它却会成为一个障碍。
一、跨域访问的原理与同源策略
1. 什么是同源策略
同源策略是浏览器安全模型的核心,它规定两个页面只有在“协议 + 域名 + 端口”完全相同的情况下,才能相互访问资源或执行脚本。例如:
同源:
https://www.example.com:443 与 https://www.example.com
不同源:
http://www.example.com 与 https://www.example.com(协议不同)
https://api.example.com 与 https://www.example.com(域名不同)
https://www.example.com:8080 与 https://www.example.com:443(端口不同)
2. 为什么会被拦截
当前端页面向不同源发送 XMLHttpRequest(XHR)或 fetch 请求时,如果服务器未返回正确的跨域响应头,浏览器会直接阻止前端读取响应结果,甚至在预检请求(OPTIONS)阶段就中断。
二、跨域访问被拦截的常见场景
前后端分离部署:前端运行在 https://web.example.com,后端 API 在 https://api.example.com,两者域名不同,天然跨域。
调用第三方 API:例如调用地图服务、支付接口、天气查询等,如果第三方服务未配置允许跨域,就会被拦截。
端口不同的本地调试:前端运行在 localhost:3000,后端运行在 localhost:8080,虽然是同一主机,但端口不同,依然算跨域。
三、前端解决方案
前端虽然无法绕过浏览器的安全限制,但可以通过一些技术手段配合后端实现跨域访问。
1. JSONP(仅限GET请求)
JSONP 利用 <script> 标签不受同源策略限制的特性,将数据以 JavaScript 代码的形式返回,从而实现跨域。优点是简单快速,适用于老旧浏览器。缺点是仅支持 GET 请求,存在一定安全风险
示例:
function handleResponse(data) {
console.log(data);
}
var script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleResponse';
document.body.appendChild(script);
2. CORS 配合服务器响应头
CORS(跨域资源共享)是目前最主流的跨域解决方案。前端无需特殊处理,只要服务器返回正确的 Access-Control-Allow-Origin 等头信息,浏览器就会允许跨域访问。
前端调用示例:
fetch('https://api.example.com/data', {
method: 'GET',
credentials: 'include'
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
3. 使用代理转发
在本地开发中,可以通过 Webpack、Vite、Nginx 等工具配置代理,将跨域请求转发到同源路径,从而绕过浏览器限制。
Vite 配置示例(vite.config.js):
export default {
server: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
}
四、后端解决方案
跨域问题的根源在于浏览器端,但真正的解决办法往往要靠后端。
1. 配置 CORS 响应头
不同语言和框架有不同的配置方式:
Node.js(Express)
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'https://web.example.com',
credentials: true
}));
Nginx
location / {
add_header 'Access-Control-Allow-Origin' 'https://web.example.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Allow-Credentials' 'true';
}
2. 处理预检请求
对于非简单请求(如 POST + JSON、带自定义头部等),浏览器会在正式请求前发送 OPTIONS 请求,后端必须正确响应,否则跨域会失败。
示例(Node.js):
app.options('*', (req, res) => {
res.header('Access-Control-Allow-Origin', 'https://web.example.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.sendStatus(200);
});
3. 反向代理
后端可以在 API 网关或服务器层做反向代理,让浏览器访问的始终是同源地址,由服务器去请求不同源的目标服务。
Nginx 示例:
location /api/ {
proxy_pass https://api.example.com/;
}
五、安全与性能注意事项
1.不要设置 Access-Control-Allow-Origin: *
处理敏感数据。如果涉及登录态或私密信息,必须指定允许的来源,并开启 Access-Control-Allow-Credentials: true
。
2.限制请求方法和头部,只开放必要的 HTTP 方法和自定义头部,减少安全风险。
3.结合认证和防爬虫机制,对跨域 API 访问添加 Token 验证、IP 限制或速率限制,防止被恶意调用。
4.缓存预检响应,使用 Access-Control-Max-Age
减少预检请求次数,提高性能。
5.跨域访问被拦截,本质是浏览器出于安全考虑执行的同源策略限制。解决问题既要理解浏览器的工作机制,也要结合业务场景灵活选用前端和后端方案。
在日常开发中,推荐优先使用 CORS + 合理的安全配置,配合代理转发作为辅助手段。对于不支持 CORS 的旧系统,可以考虑 JSONP 作为权宜之计,但要注意数据安全和请求类型的限制。只要前后端配合合理,就可以既保证数据安全,又让网站跨域访问顺畅无阻。