前言
在现代Web开发中,前后端分离架构已经成为主流。前端应用通常部署在example.com,后端API部署在api.example.com,这种跨域场景必然遇到CORS(跨域资源共享)问题。同时,API身份验证往往使用JWT(JSON Web Token)方案。当CORS与JWT同时配置时,很多开发者会遇到跨域请求中Token丢失、预检请求失败等棘手问题。本文将手把手教你如何在Nginx中正确配置CORS与JWT Token认证,让跨域身份验证畅通无阻。
一、CORS与JWT基础概念
什么是CORS
CORS(Cross-Origin Resource Sharing,跨域资源共享)是一种基于HTTP头的安全机制,允许服务器声明哪些源站有权限访问哪些资源。浏览器会针对跨域请求自动发起CORS预检(preflight),发送一个OPTIONS请求,询问服务器是否允许实际请求。
什么是JWT Token
JWT(JSON Web Token)是一种紧凑的、自包含的Token格式,用于在各方之间安全地传输信息。一个JWT Token由三部分组成:Header(头部)、Payload(载荷)、Signature(签名)。前端在请求头中携带Authorization: Bearer <token>来完成后端身份验证。
为什么CORS与JWT会冲突
当JWT Token放在Authorization头中时,这属于"非简单请求头",浏览器会先发OPTIONS预检请求。如果Nginx的CORS配置不当,预检请求会被拒绝,或者实际请求的Authorization头被浏览器拦截,导致Token无法传递到后端。
二、Nginx CORS基础配置回顾
在配置JWT认证之前,先确保CORS基础配置正确。以下是最常用的Nginx CORS配置模板:
location /api/ {
# CORS基础头
add_header 'Access-Control-Allow-Origin' '$http_origin' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Accept' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' '86400' always;
# 处理OPTIONS预检请求
if ($request_method = 'OPTIONS') {
return 204;
}
# 反向代理到后端
proxy_pass http://backend;
}
关键点:Access-Control-Allow-Headers必须显式包含Authorization,否则浏览器不允许前端在请求头中携带JWT Token。
三、JWT Token在Nginx中的验证方式
Nginx本身不解析JWT Token内容(除非使用OpenResty/Lua),常见的JWT处理方案有三种:
方案一:Nginx仅做CORS,JWT验证交给后端
这是最简单、最常用的方案。Nginx负责处理CORS跨域,JWT的签发和验证全部由后端应用(如Node.js、Java、PHP)完成。Nginx只需要正确传递Authorization请求头即可。
方案二:使用OpenResty+Lua在Nginx层验证JWT
如果希望在Nginx层就拦截无效Token,可以使用OpenResty的lua-resty-jwt库。这种方式能减少无效请求到达后端的流量,适合高并发场景。
方案三:使用Nginx Auth Request模块
Nginx的auth_request模块可以将Token验证委托给一个独立的验证子请求(转发到验证服务),验证通过才允许访问实际资源。
四、完整实战:Nginx CORS + JWT 配置方案
下面以方案一(Nginx做CORS,后端验证JWT)为例,给出生产环境可直接使用的完整配置。
4.1 基础CORS + 反向代理配置
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location /api/ {
# === CORS配置开始 ===
set $cors_origin 'https://www.frontend.com';
if ($http_origin ~ '^https://(www\.frontend\.com|admin\.frontend\.com)$') {
set $cors_origin $http_origin;
}
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS, PATCH' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Accept, X-Requested-With' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Expose-Headers' 'Authorization' always;
add_header 'Access-Control-Max-Age' '86400' always;
# === CORS配置结束 ===
# 处理OPTIONS预检请求
if ($request_method = 'OPTIONS') {
return 204;
}
# 将Authorization头传递给后端(关键!)
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;
# 其他标准代理头
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:8080;
}
}
4.2 配置逐行解析
| 配置项 | 作用 |
|---|---|
Access-Control-Allow-Origin | 允许跨域请求的源。携Cookie时不能用通配符*,必须明确指定域名。 |
Access-Control-Allow-Headers | 明确允许Authorization头,前端才能成功携带JWT Token。 |
Access-Control-Allow-Credentials | 设为true时,前端请求需要设置withCredentials: true,Cookie和HTTP认证信息才会被发送。 |
Access-Control-Expose-Headers | 允许前端JavaScript访问的响应头。如果后端在响应头中返回新的Token,需要在此暴露。 |
OPTIONS返回204 | 预检请求不需要转发到后端,Nginx直接返回204 No Content,减轻后端压力。 |
proxy_set_header Authorization | 关键中的关键:将前端传来的Authorization头原样传递给后端,否则后端拿不到JWT Token。 |
五、前端如何正确携带JWT Token
后端和Nginx配置好后,前端也需要正确配置才能携带Token。以fetch为例:
// 正确示例:携带JWT Token的跨域请求
fetch('https://api.example.com/api/user/profile', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('jwt_token'),
'Content-Type': 'application/json'
},
credentials: 'include' // 如果需要携带Cookie,必须设置此项
})
.then(res => res.json())
.then(data => console.log(data));
注意:如果Nginx配置了Access-Control-Allow-Credentials: true,前端的credentials必须设为'include',否则Cookie不会被发送。
六、使用OpenResty在Nginx层验证JWT(进阶方案)
如果你使用OpenResty(Nginx+Lua),可以在Nginx层直接验证JWT签名,无效Token直接拒绝,不转发到后端。
location /api/ {
access_by_lua_block {
local jwt = require "resty.jwt"
local token = ngx.var.http_authorization
if not token then
ngx.status = 401
ngx.header["Access-Control-Allow-Origin"] = ngx.var.http_origin
ngx.header["Access-Control-Allow-Credentials"] = "true"
ngx.say('{"error":"missing token"}')
return ngx.exit(401)
end
-- 去掉 "Bearer " 前缀
local token_str = token:sub(8)
local verified = jwt:verify("your-secret-key", token_str)
if not verified.verified then
ngx.status = 401
ngx.header["Access-Control-Allow-Origin"] = ngx.var.http_origin
ngx.header["Access-Control-Allow-Credentials"] = "true"
ngx.say('{"error":"invalid token"}')
return ngx.exit(401)
end
-- Token有效,将payload传递给后端
ngx.req.set_header("X-User-Id", verified.payload.sub)
}
proxy_pass http://backend;
}
七、常见问题与解决方法
问题1:前端控制台报错"Authorization header is not allowed"
原因:Access-Control-Allow-Headers中没有包含Authorization。
解决:在Nginx配置中添加add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
问题2:OPTIONS预检请求返回401/403
原因:预检请求被后端的JWT验证中间件拦截了。OPTIONS请求不带Token,但后端中间件对所有请求都验证Token。
解决:后端中间件对OPTIONS请求跳过验证;或者像本文配置一样,在Nginx层直接返回204,不转发OPTIONS到后端。
问题3:跨域请求能成功,但Response Header里拿不到Authorization
原因:没有配置Access-Control-Expose-Headers。浏览器默认只允许前端访问简单响应头(Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma)。
解决:添加add_header 'Access-Control-Expose-Headers' 'Authorization' always;
问题4:设置了withCredentials但还是无法携带Cookie
原因:Access-Control-Allow-Origin使用了通配符*。当请求需要携带凭证(Cookie、HTTP认证)时,CORS规范不允许使用*。
解决:将Access-Control-Allow-Origin改为明确的域名,如https://www.frontend.com。
八、安全最佳实践
- 不要用
*作为Allow-Origin:生产环境务必明确指定允许的域名,避免任意网站都能跨域访问你的API。 - JWT Secret要足够复杂:使用至少32位的随机字符串作为JWT签名密钥,防止Token被伪造。
- 设置JWT过期时间:Access Token有效期建议设为15-30分钟,使用Refresh Token机制续期。
- HTTPS everywhere:JWT Token在传输过程中必须走HTTPS,防止被中间人劫持。
- 避免在Token中存储敏感信息:JWT的Payload是Base64编码(非加密),任何人都可以解码查看,不要放密码、身份证号等敏感数据。
- 使用
HttpOnly+SecureCookie存储Token:如果Token通过Cookie传递,务必设置HttpOnly(防XSS)和Secure(仅HTTPS)属性。
九、相关文章推荐
如果你对Nginx CORS配置的更多细节感兴趣,可以阅读以下相关文章:
- Nginx CORS跨域配置详解:从原理到实战的完整指南
- Nginx CORS与Cookie携带配置教程:解决跨域请求Cookie丢失的完整实战
- Nginx CORS多域名动态匹配:if与map两种方案深度对比与实战选择
- Nginx CORS携带自定义Header配置教程:解决跨域请求自定义Header丢失的完整实战
十、总结
Nginx配置CORS与JWT Token认证的核心要点可以归纳为三句话:
Access-Control-Allow-Headers必须包含Authorization,否则前端无法携带Token;- OPTIONS预检请求要在Nginx层直接处理(返回204),不要转发到后端;
- 使用
proxy_set_header Authorization $http_authorization;将Token传递给后端,这一步最容易被遗漏。
按照本文的配置模板操作,你可以快速搭建起一个同时支持CORS跨域和JWT身份验证的Nginx反向代理层。如果在配置过程中遇到其他问题,欢迎在评论区留言讨论。
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论