修改反向代理Header解决源服务器CORS问题
目录
遇上服务器带 CORS 运行,同时前端在 dev(localhost)的时候,访问线上的服务器会出现CORS问题。
以下服务器都为测试服务器,并非实际服务器域名
场景 | CORS |
---|---|
线上服务器 | production.example.net |
本地前端Origin | localhost:3000 |
上表格显示服务器验证的CORS跟本地前端的Origin不一样,所以前端向服务器发送 XHR 请求时会被CORS阻拦。
反向代理的工作
1. 前端发送请求
前端Origin | 反代 | 线上服务器 |
---|---|---|
localhost:3000 | 无 | ❌ 不匹配,响应空的CORS |
localhost:3000 | Origin 改为 production.example.net | ✅ 匹配,返回 production.example.net |
根据上表,当发起请求的时候,反代的工作是在代理的时候,将Origin写成线上服务器匹配的域。
2. 前端接收响应
线上服务器 | 反代 | 前端 |
---|---|---|
返回正常数据 | 无 | ❌ 因为CORS为空,浏览器阻止请求 |
返回正常数据 | CORS改为* |
✅ CORS匹配本地域,请求有效 |
根据上表,当收到响应的时候,反代的工作是将响应头的CORS改为前端匹配的域。
Nginx Conf
# Nginx 运行在前端所在的机器,不加密的HTTP协议
server {
# 监听8000端口
listen *:8000;
# default_server表示无论以IP地址还是域名访问nginx,都处理这个请求
server_name default_server;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
# 改写收到的请求,让线上服务器识别
proxy_set_header Origin https://production.example.net;
# 先隐藏线上服务器发来的CORS
proxy_hide_header 'access-control-allow-origin';
# 再添加自定义的CORS响应头
# always 表示不管线上服务器是否返回200都加上这个头
add_header 'access-control-allow-origin' '*' always;
# 代理线上服务器的流量
proxy_pass https://production.example.net;
}
}
Node 版
Nginx 简单配之后可以在 docker 里面运行。这里的 NodeJS 适用没有 docker 的开发环境。
var http = require('http'), https = require('https'), uri = require('url')
// 目标
var TARGET = 'https://example.com'
// 反代端口
var SERVER_PORT = 8000
var server = http.createServer()
server.on('request', function(req, rsp) {
// 移除带有前端Origin的Header
delete req.headers['host']
delete req.headers['referer']
delete req.headers['origin']
if (req.method === 'OPTIONS') return rsp.writeHead(200,{
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': '*',
}).end()
var url = uri.parse(TARGET + req.url)
var proxy = https.request({
...url,
method: req.method,
headers: req.headers,
}, function(proxyRsp) {
rsp.setHeader('Access-Control-Allow-Origin', '*')
rsp.setHeader('Access-Control-Allow-Headers', '*')
rsp.setHeader('Access-Control-Allow-Methods', '*')
for (var key in proxyRsp.headers) {
rsp.setHeader(key, proxyRsp.headers[key])
}
rsp.writeHead(proxyRsp.statusCode)
proxyRsp
.on('data', function (chunk) { rsp.write(chunk) })
.on('end', function () { rsp.end() })
})
// 反代的请求出错 = 500
proxy.once('error', function (e) {
console.error('Proxy error:', e.toString())
rsp.writeHead(500).end()
})
// 前端关掉了请求,则反代的请求也关掉
rsp.on('close', function () { proxy.destroy()})
// 把前端的数据转发到反代
req
.on('data', chunk => proxy.write(chunk))
.on('end', () => proxy.end())
})
server.listen(SERVER_PORT)
参考
How to add a response header on nginx when using proxy_pass?