什么是ref="/tag/137/" style="color:#C468A7;font-weight:bold;">JavaScript跨域问题
在开发网页应用时,你可能会遇到浏览器报出这样的错误:"No 'Access-Control-Allow-Origin' header is present"。这其实就是JavaScript的跨域问题。简单来说,当你在一个域名下请求另一个域名的数据时,比如从 https://site-a.com 向 https://api.site-b.com 发起 AJAX 请求,浏览器出于安全考虑会阻止这个请求,除非对方服务器明确允许。
这种机制叫“同源策略”,是浏览器保护用户数据不被随意窃取的重要手段。所谓“同源”,指的是协议、域名、端口三者完全相同。哪怕只是端口不同,比如 http://localhost:3000 和 http://localhost:4000,也算跨域。
常见的跨域场景
你在本地调试前端项目时,后端接口通常跑在另一个端口上。比如你用 Vue 或 React 开发,前端是 http://localhost:5173,而接口地址是 http://localhost:3000/api/users,这时候一发请求就跨域了。再比如,你的网站想调用第三方地图 API 或天气服务,如果对方没配置 CORS,也会失败。
CORS:跨域资源共享
最标准的解决方式是通过 CORS(Cross-Origin Resource Sharing)。它需要后端配合,在响应头中加入允许跨域的字段。例如:
Access-Control-Allow-Origin: https://your-site.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization如果你能控制后端代码,Node.js 的 Express 框架可以这样设置:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://your-site.com');
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});开发阶段也可以直接允许所有来源(仅限测试):
res.header('Access-Control-Allow-Origin', '*');JSONP:老但有效的办法
在 CORS 出现之前,大家常用 JSONP 来绕过跨域限制。它的原理是利用 <script> 标签不受同源策略影响的特性,动态插入一个脚本,回调函数名由前端指定。
比如你想获取用户数据:
function handleUserData(data) {
console.log('用户信息:', data);
}
const script = document.createElement('script');
script.src = 'https://api.example.com/user?callback=handleUserData';
document.head.appendChild(script);后端返回的内容必须是函数调用的形式:
handleUserData({"name": "张三", "age": 28});不过 JSONP 只支持 GET 请求,安全性也较差,现在多用于兼容老旧系统。
代理服务器:开发环境利器
如果你没法改后端,又不想上线前一直被跨域困扰,可以在开发时用代理。比如在 Vite 项目中配置 vite.config.js:
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
}
}
}
}这样一来,你原本要请求 /api/users,实际会被转发到 http://localhost:3000/api/users,因为请求是从本地服务器发出的,不走浏览器直接跨域,自然就没问题。
Nginx 反向代理部署方案
上线后也可以用 Nginx 做反向代理。比如把所有 /api/ 开头的请求转给真正的后端服务:
location /api/ {
proxy_pass http://backend-server:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}这样前端和 API 看起来是同一个域名,彻底避开跨域问题。
避免踩坑的小建议
发送携带 Cookie 的请求时,前端要设置 credentials: 'include',同时后端的 Access-Control-Allow-Origin 不能为 *,必须是具体域名,否则浏览器会拒绝。
fetch 示例:
fetch('https://api.example.com/login', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username: 'test', pwd: '123' })
})对应的后端响应头应包含:
Access-Control-Allow-Origin: https://your-site.com
Access-Control-Allow-Credentials: true