前端跨域请求解决方案

理解跨域原理,掌握 CORS、Nginx 代理、Node 中间件等常用跨域方案

问题

前端如何实现跨域请求?

解答

什么是跨域

同源策略要求”协议+域名+端口”三者相同,任意一个不同都算跨域。跨域请求能发出去,服务端也能正常返回结果,但响应会被浏览器拦截。

允许跨域加载资源的标签:<img><link><script>

CORS

CORS 是最常用的跨域方案,关键在于服务器配置。分为简单请求和非简单请求。

简单请求

同时满足以下条件:

  • 请求方法是 HEAD、GET、POST 之一
  • 请求头不超过 Accept、Accept-Language、Content-Language、Content-Type(仅限 application/x-www-form-urlencoded、multipart/form-data、text/plain)

服务器响应头示例:

Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar

非简单请求

PUT、DELETE 请求或 Content-Type 为 application/json 时,浏览器会先发送 OPTIONS 预检请求。

减少 OPTIONS 请求次数的方法:

Access-Control-Max-Age: 86400

携带 Cookie

需要同时满足:

  • 服务器设置 Access-Control-Allow-Credentials: true
  • 客户端设置 xhr.withCredentials = true
  • Access-Control-Allow-Origin 不能为 *,必须指定明确域名

Nginx 代理

配置代理服务器转发请求:

server { 
    listen 81;
    server_name www.domain1.com;
    location / { 
        proxy_pass http://domain2.com:8080;
        proxy_cookie_domain www.domain1.com www.domain2.com;
        add_header Access-Control-Allow-Origin http://www.domain2.com;
        add_header Access-Control-Allow-Credentials true;
    } 
}

Node 中间件代理

Vue 项目中配置 vue.config.js

module.exports = {
    devServer: {
        proxy: {
            '/api': {
                target: 'http://example.com:8080',
                changeOrigin: true
            }
        }
    }
}

Express 中使用:

const express = require('express')
const proxy = require('http-proxy-middleware')
const app = express()

app.use('/', proxy({ 
    target: 'http://example.com:8080',
    changeOrigin: true,
    onProxyRes: function(proxyRes, req, res) { 
        res.header('Access-Control-Allow-Origin', 'http://example.com')
        res.header('Access-Control-Allow-Credentials', 'true')
    }
}))

JSONP

利用 <script> 标签不受同源策略限制的特性:

let script = document.createElement('script')
script.src = 'http://example.com/api?callback=handleCallback'
document.body.appendChild(script)

function handleCallback(res) {
    console.log(res)
}

服务器返回:

handleCallback({ code: 200, data: [] })

缺点:只支持 GET 请求,存在 XSS 风险。

WebSocket

WebSocket 协议不实行同源策略,使用 ws://wss:// 前缀。服务器可通过请求头的 Origin 字段判断是否允许通信。

postMessage

用于跨域通信场景:页面与新窗口、多窗口之间、页面与 iframe 之间。

// 发送方
targetWindow.postMessage(data, 'http://example.com')

// 接收方
window.addEventListener('message', function(e) {
    if (e.origin === 'http://example.com') {
        console.log(e.data)
    }
})

通过 SameSite 属性控制:

Set-Cookie: session=123; SameSite=None; Secure

SameSite 取值:

  • Strict:完全禁止第三方 Cookie
  • Lax:允许 a 标签跳转、link 标签、GET 表单提交
  • None:允许跨域发送,但必须配合 Secure(仅 HTTPS)

Chrome 80 后默认值从 None 改为 Lax。

关键点

  • 跨域是浏览器的同源策略限制,请求能发出但响应被拦截
  • CORS 是最标准的方案,分简单请求和非简单请求(需预检)
  • 开发环境常用 Nginx 或 Node 中间件代理,生产环境用 CORS
  • JSONP 只支持 GET 请求,已逐渐被 CORS 替代
  • Cookie 跨域需设置 SameSite 属性,Chrome 80 后默认为 Lax