Nginx+code-server反向代理下非浏览器POST请求返回401排查
Stripe Webhook接口返回401错误排查方案
问题背景
2023年4月28日编辑:补充Nginx相关信息
在React/Node项目中集成Stripe支付时,服务器需处理两类POST请求:
/create-payment-intent:来自浏览器的请求,运行正常/webhook:来自Stripe服务器的请求,返回HTTP 401错误,请求未到达Express处理器
环境架构
请求需经过两层反向代理:
- Nginx将
https://<my-code-server-domain>的请求转发至localhost:8082的code-server - code-server将
https://<my-code-server-domain>/proxy/3024/*的请求转发至localhost:3024的Node应用
请求示例:
https://<my-code-server-domain>/proxy/3024/create-payment-intenthttps://<my-code-server-domain>/proxy/3024/webhook
服务器代码片段
const app = express() app.post("/webhook", express.raw({type: 'application/json'}), async (req, res) => { let event = req.body; ...
排查现状
- 断点显示webhook请求未进入Express处理器,说明在到达处理器前被拦截
- 已尝试添加cors模块及手动设置宽松CORS头部,无效
- 使用Stripe官方转发工具测试,结果一致
- Nginx access.log显示:
/proxy/3024/create-payment-intent返回200,/proxy/3024/webhook返回401 - Nginx debug日志显示401是从上游(code-server)返回的
排查方向与解决方案
1. 解除code-server代理的身份验证拦截
code-server的/proxy/*路径默认会启用身份验证拦截,浏览器请求携带了code-server的会话Cookie所以能通过,但Stripe的webhook请求无此Cookie,因此被拦截返回401。
解决方法:
- 修改code-server的
config.yaml配置,将webhook路径加入免认证白名单:auth: bypass: - "/proxy/3024/webhook" - 重启code-server生效
2. 检查Node应用的中间件拦截
确认Node应用中是否存在全局身份验证中间件,误拦截了webhook请求。需确保/webhook路径跳过所有无关的认证中间件,仅保留必要的express.raw解析中间件。
3. 验证Nginx代理配置
确保Nginx正确传递请求头(尤其是Stripe-Signature),且不修改原始请求体(Stripe签名验证依赖原始请求内容)。示例配置片段:
location / { proxy_pass http://localhost:8082; 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_request_buffering off; # 禁用请求缓冲,避免修改请求体 }
4. 直接测试Node应用接口
在服务器本地用curl直接请求Node应用的webhook接口,确认接口本身是否正常:
curl -X POST http://localhost:3024/webhook \ -H "Content-Type: application/json" \ -d '{"type": "payment_intent.succeeded"}'
如果返回200,说明问题确实出在代理层;如果仍返回401,排查Node应用内部的拦截逻辑。
5. 后续补充Stripe签名验证
当请求能到达Express处理器后,需添加Stripe官方签名验证逻辑,避免伪造请求:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY); app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => { const sig = req.headers['stripe-signature']; let event; try { event = stripe.webhooks.constructEvent( req.body, sig, process.env.STRIPE_WEBHOOK_SECRET ); } catch (err) { return res.status(400).send(`Webhook Error: ${err.message}`); } // 处理对应事件逻辑 res.json({received: true}); });
内容的提问来源于stack exchange,提问作者vicmortelmans
相关产品推荐
相关产品推荐

