已配置Access-Control-Allow-Origin仍遇NextJS接口CORS错误求助
NextJS接口CORS预检请求失败问题原因分析
问题背景
React前端调用NextJS接口时触发CORS错误,提示预检请求未通过HTTP ok状态校验,具体表现:
- 已在
next.config.js配置包含Access-Control-Allow-Origin在内的CORS响应头 - 前端通过axios发起POST请求,携带
Authorization(Bearer Token格式)及Content-Type头时触发CORS错误 - 移除
Authorization头后无CORS错误,但接口返回403未授权 - Postman调用该接口正常,其他NextJS接口无此问题
相关代码如下:
next.config.js配置
module.exports = { reactStrictMode: true, webpack: (config, options) => { if (!options.isServer) { config.resolve.fallback.fs = false } return config }, async headers() { return [ { source: '/api/:path*', headers: [ { key: 'origins', value: '*' }, { key: 'Bypass-Tunnel-Reminder', value: '*' }, { key: 'Access-Control-Allow-Origin', value: '*' }, { key: 'Access-Control-Request-Methods', value: 'POST, GET, OPTIONS' }, { key: 'Access-Control-Allow-Headers', value: 'Authorization, Content-Type' }, ], }, ] }, }
前端请求代码
axios.post( 'http://localhost:3001/github/setCredentials', { accessCode, }, { headers: { Authorization: localStorage.getItem('jwt_token'), 'Content-Type': 'application/x-www-form-urlencoded', }, }, )
接口处理代码
const authenticate = (method, req, res) => new Promise((resolve, reject) => { passport.authenticate(method, { session: false }, (error, token) => { if (error) { reject(error) } else { resolve(token) } })(req, res) }) passport.use(jwtStrategy) const handler = nextConnect({ onError: (err, req: NextApiRequest, res: NextApiResponse, next) => { console.error(err.stack) res.status(500).end('Something broke!') }, onNoMatch: (req: NextApiRequest, res: NextApiResponse) => { res.status(404).end('Page is not found') }, }) .use(passport.initialize()) .post(async (req: NextApiRequest, res: NextApiResponse) => { try { const user = await authenticate('jwt', req, res) if (!user) return res.status(403).json({ error: 'Unauthenticated user' }) return res.status(200).json({ success: true }) } catch (err) { return res.status(500).json({ error: err.toString() }) } }) export default handler
Passport JWT策略代码
const opts: any = {} opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken() opts.secretOrKey = JWT_SECRET export const jwtStrategy = new JwtStrategy(opts, function (jwt_payload, done) { mongoose .getUserByEthAddress(jwt_payload.address) .then((user: User | null) => { return done(null, user) }) .catch((err) => { return done(err, false) }) })
问题原因
未处理OPTIONS预检请求
当请求携带自定义头(如Authorization)或非简单Content-Type时,浏览器会先发送OPTIONS预检请求,确认服务器是否允许该请求。你的接口仅定义了POST方法的处理逻辑,没有处理OPTIONS请求,导致预检请求返回404或非2xx状态码,浏览器判定CORS校验失败。CORS头配置存在错误
- 头字段名错误:
Access-Control-Request-Methods应为Access-Control-Allow-Methods(前者是预检请求中浏览器发送的字段,后者是服务器响应应返回的允许方法字段) - 多余的
origins字段:标准CORS头为Access-Control-Allow-Origin,origins字段无实际作用
- 头字段名错误:
可能的Token格式问题(次要)
如果localStorage中存储的仅为纯Token字符串,未拼接Bearer前缀,会导致Passport JWT无法正确提取Token,进而触发403错误,但这不是CORS问题的直接原因。
解决办法
1. 在接口中添加OPTIONS请求处理
通过nextConnect的options方法直接返回200状态及正确的CORS头:
const handler = nextConnect({ onError: (err, req: NextApiRequest, res: NextApiResponse, next) => { console.error(err.stack) res.status(500).end('Something broke!') }, onNoMatch: (req: NextApiRequest, res: NextApiResponse) => { res.status(404).end('Page is not found') }, }) .use(passport.initialize()) // 新增OPTIONS请求处理 .options(async (req, res) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type'); res.status(200).end(); }) .post(async (req: NextApiRequest, res: NextApiResponse) => { try { const user = await authenticate('jwt', req, res) if (!user) return res.status(403).json({ error: 'Unauthenticated user' }) return res.status(200).json({ success: true }) } catch (err) { return res.status(500).json({ error: err.toString() }) } })
2. 修正next.config.js中的CORS头配置
修正错误字段名并移除无用字段:
async headers() { return [ { source: '/api/:path*', headers: [ { key: 'Bypass-Tunnel-Reminder', value: '*' }, { key: 'Access-Control-Allow-Origin', value: '*' }, { key: 'Access-Control-Allow-Methods', value: 'POST, GET, OPTIONS' }, { key: 'Access-Control-Allow-Headers', value: 'Authorization, Content-Type' }, ], }, ] },
3. 确保Token格式正确
如果localStorage存储的是纯Token,需拼接Bearer 前缀:
Authorization: `Bearer ${localStorage.getItem('jwt_token')}`,
内容的提问来源于stack exchange,提问作者leojacoby
相关产品推荐
相关产品推荐

