含查询字符串的Twilio Webhook签名验证403失败求助
问题描述
发送Twilio SMS时使用带查询字符串的状态回调URL:
const TwilioClient = Twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); TwilioClient.messages.create({ body: message, from: fromNumber, to: toNumber, statusCallback: 'https://example.com/webhook/twilio?id=12345', });
使用Twilio Express中间件处理请求并验证签名:
router.post( '/twilio', twilio.webhook({ authToken: process.env.TWILIO_AUTH_TOKEN, host: 'example.com', validate: true, }), async (req, res) => { /* ... */ }, );
带查询字符串时请求返回403,去掉后验证正常。断点发现中间件用带查询字符串的URL验证,但尝试多种URL变体仍无法解决,使用twilio包v4.12.0版本。
解决方案
问题出在Twilio签名验证依赖完整请求URL(含查询字符串),但默认的Express中间件构造验证URL时会忽略查询参数,导致签名不匹配。只需配置getUrl自定义函数,让中间件使用请求的完整URL进行验证:
router.post( '/twilio', twilio.webhook({ authToken: process.env.TWILIO_AUTH_TOKEN, host: 'example.com', validate: true, // 自定义获取验证用URL的逻辑 getUrl: (req) => { return `https://${req.headers.host}${req.originalUrl}`; } }), async (req, res) => { /* ... */ }, );
req.originalUrl会保留请求的完整路径(包括查询字符串),结合请求头里的host构造出和Twilio发送时一致的URL,就能通过签名验证。
如果你的部署环境使用非标准HTTPS端口(比如本地测试的3000端口),可以根据实际情况调整URL的协议和端口部分。
内容的提问来源于stack exchange,提问作者asportnoy
相关产品推荐
相关产品推荐

