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

部署后登录接口遇CORS拦截及ERR_HTTP_HEADERS_SENT错误求助

部署人脸识别项目后登录接口遭遇CORS拦截及服务器报错问题

问题描述

我是JavaScript新手,近期部署了人脸识别项目,开发环境下一切正常,但部署后登录时遭遇CORS拦截(注册功能可正常使用,数据库已确认连接正常)。

CORS配置及登录接口代码

app.use(cors({
  origin:["https://almoghasson.github.io/Face-Recognition/","http://localhost:3001"],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
}));

app.post('/signin', (req, res) => {
  const { email, psw } = req.body 
  const user = { email: email } //

  postgres.select('email', 'hash').from('login')
    .where('email', '=', email) 
    .then(data => {
      const isValid = bcrypt.compareSync(psw, data[0].hash)

      if (isValid) {
        const refreshToken = uuid();
        // store token in db & cookie
        postgres.select('*').from('users')
          .where('email', '=', email)
          .update({
            token: refreshToken
          })
          .catch(err => res.status(400).json(err))

        //return user data
        return postgres.select('*').from('users')
          .where('email', '=', email)
          .then(user => {
            const { token: _, ...userWithoutRefreshToken } = user[0];
            const accessToken = generateAccessToken(userWithoutRefreshToken);
            res.cookie(REFRESH_TOKEN_COOKIE_NAME, refreshToken, { httpOnly: false, maxAge: 24 * 60 * 60 * 1000, sameSite: "none", path: '/', secure: true });
            res.json({ accessToken });
          })
          .catch(err => res.status(400).json('unable to get user'))
      } else {
        res.status(400).json('wrong credentials')
      }
    })
    .catch(err => res.status(400).json('wrong credentials'))
});

前端Fetch代码

fetch('https://face-recognition-api-n3yg.onrender.com/signin', {
            method:'post',
            headers : {
                'Content-Type':'application/json',
            },
            body: JSON.stringify({
                email: this.state.signInEmail,  
                psw: this.state.signInPsw
            }),
            credentials: 'include'
        })
        .then(res => res.json())
        .then(res => {
            if (res.accessToken){
                const user = decodeAndStoreJWT(res.accessToken);
                this.props.setBearer(res.accessToken)
                this.props.loadUser(user)
                this.props.onRouteChange('home')
                this.props.runInterval()
            }
        })

疑惑点

  • 配置的origin未被接受,原因是什么?
  • 页面URL为.../face-recognition/,但实际请求的origin仅为...github.io,这是为什么?

已尝试的解决方案

  • 在前端代码中添加access-allow-origin请求头,无效
  • 将CORS的origin设为"*",无效,且该方案不推荐

服务器控制台错误日志

Nov 1 06:41:45 PM  Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers 
after they are sent to the client
Nov 1 06:41:45 PM      at new NodeError (node:internal/errors:372:5)
Nov 1 06:41:45 PM      at ServerResponse.setHeader (node:_http_outgoing:576:11)
Nov 1 06:41:45 PM      at ServerResponse.header (/opt/render/project/src/Backend/node_modules/express/lib/response.js:794:10)
Nov 1 06:41:45 PM      at ServerResponse.send (/opt/render/project/src/Backend/node_modules/express/lib/response.js:174:12)
Nov 1 06:41:45 PM      at ServerResponse.json (/opt/render/project/src/Backend/node_modules/express/lib/response.js:278:15)
Nov 1 06:41:45 PM      at /opt/render/project/src/Backend/server.js:86:45
Nov 1 06:41:45 PM      at processTicksAndRejections (node:internal/process/task_queues:96:5) {
Nov 1 06:41:45 PM    code: 'ERR_HTTP_HEADERS_SENT'
Nov 1 06:41:45 PM  }

问题分析与解决

1. Origin配置不生效的核心原因

  • Origin匹配规则错误:浏览器发送的Origin仅包含协议、域名、端口,不包含URL路径。你配置的https://almoghasson.github.io/Face-Recognition/带了路径,无法匹配实际请求的https://almoghasson.github.io,导致CORS拦截。
  • 服务器重复响应报错:登录接口中,update操作的catch会提前返回错误响应,后续代码仍会执行并再次发送响应,触发ERR_HTTP_HEADERS_SENT错误,导致浏览器无法正确解析CORS响应头,进一步加剧拦截问题。

2. 页面URL带路径但Origin是根域名的原因

浏览器的Origin定义是协议+域名+端口,不管页面在域名下的哪个子路径,请求的Origin都是根域名,这是浏览器的标准安全行为。

3. 具体修复步骤

修复CORS配置

将origin数组中的带路径项改为根域名:

app.use(cors({
  origin:["https://almoghasson.github.io","http://localhost:3001"],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
}));

修复登录接口的重复响应问题

把update和select改为链式调用,避免并行执行导致的多次响应:

app.post('/signin', (req, res) => {
  const { email, psw } = req.body 

  postgres.select('email', 'hash').from('login')
    .where('email', '=', email) 
    .then(data => {
      const isValid = bcrypt.compareSync(psw, data[0].hash)

      if (isValid) {
        const refreshToken = uuid();
        // 先执行更新操作,完成后再查询用户数据
        return postgres.select('*').from('users')
          .where('email', '=', email)
          .update({ token: refreshToken })
          .then(() => postgres.select('*').from('users').where('email', '=', email))
          .then(user => {
            const { token: _, ...userWithoutRefreshToken } = user[0];
            const accessToken = generateAccessToken(userWithoutRefreshToken);
            res.cookie(REFRESH_TOKEN_COOKIE_NAME, refreshToken, { httpOnly: false, maxAge: 24 * 60 * 60 * 1000, sameSite: "none", path: '/', secure: true });
            res.json({ accessToken });
          })
          .catch(err => res.status(400).json(err))
      } else {
        res.status(400).json('wrong credentials')
      }
    })
    .catch(err => res.status(400).json('wrong credentials'))
});

4. 额外注意事项

  • 前端不要手动设置access-allow-origin请求头,该头是服务器返回的响应头,前端设置无效。
  • 当credentials: true时,origin不能设为*,这是浏览器的安全限制,所以之前设*无效是正常的。

内容的提问来源于stack exchange,提问作者Almog Hasson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 06:50:55