You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

已配置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)
        })
})

问题原因

  1. 未处理OPTIONS预检请求
    当请求携带自定义头(如Authorization)或非简单Content-Type时,浏览器会先发送OPTIONS预检请求,确认服务器是否允许该请求。你的接口仅定义了POST方法的处理逻辑,没有处理OPTIONS请求,导致预检请求返回404或非2xx状态码,浏览器判定CORS校验失败。

  2. CORS头配置存在错误

    • 头字段名错误:Access-Control-Request-Methods应为Access-Control-Allow-Methods(前者是预检请求中浏览器发送的字段,后者是服务器响应应返回的允许方法字段)
    • 多余的origins字段:标准CORS头为Access-Control-Allow-Origin,origins字段无实际作用
  3. 可能的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 19:03:30