Next.js 14通过Ngrok接收SVIX Webhook遇308重定向问题求助
Next.js 14 接收SVIX Webhook返回308永久重定向问题
我在Next.js 14应用中无法正常接收来自SVIX的Webhook。目前使用Ngrok将请求转发至本地运行的Next.js应用,但所有HTTP请求均返回308 Permanent Redirect状态码。希望解决该问题,当前仅需实现Webhook响应日志功能。
我的Next.js API路由(app/api/recallWebhook/route.js)
import crypto from "crypto"; export async function POST(request) { try { const body = await request.json(); console.log("Webhook received:", body); const signingSecret = process.env.DEV_RECALL_WEBHOOK_SIGNING_SECRET; console.log("Signing secret:", signingSecret); const svixId = request.headers.get("svix-id"); const svixTimestamp = request.headers.get("svix-timestamp"); const svixSignature = request.headers.get("svix-signature"); const signedContent = `${svixId}.${svixTimestamp}.${JSON.stringify(body)}`; const secretBytes = Buffer.from(signingSecret.split("_")[1], "base64"); const computedSignature = crypto .createHmac("sha256", secretBytes) .update(signedContent) .digest("base64"); const isSignatureValid = svixSignature.split(" ").some((sig) => { const [version, signature] = sig.split(","); return signature === computedSignature; }); if (!isSignatureValid) { return new Response("Invalid signature", { status: 401 }); } console.log("Verified webhook:", body); return new Response( JSON.stringify({ message: "Webhook received and verified successfully", }), { status: 200, headers: { "Content-Type": "application/json", }, } ); } catch (error) { console.error("Error processing webhook", error); return new Response( JSON.stringify({ message: "Error processing webhook" }), { status: 500, headers: { "Content-Type": "application/json", }, } ); } } export function GET() { return new Response("GET method not supported", { status: 405 }); }
Ngrok服务器快照
Session Status online Account ******* (Plan: Free) Version 3.6.0 Region Europe (eu) Latency 38ms Web Interface http://127.0.0.1:4040 Forwarding https://****-***-248-85-9.ngrok-free.app -> http://localhost:3000 Connections ttl opn rt1 rt5 p50 p90 103 4 0.00 0.01 6.05 149.09 HTTP Requests ------------- POST /api/recallWebhook/ 308 Permanent Redirect POST /api/recallWebhook/ 308 Permanent Redirect POST /api/recallWebhook/ 308 Permanent Redirect POST /api/recallWebhook/ 308 Permanent Redirect POST /api/recallWebhook/ 308 Permanent Redirect POST /api/recallWebhook/ 308 Permanent Redirect POST /api/recallWebhook/ 308 Permanent Redirect POST /api/recallWebhook/ 308 Permanent Redirect POST /api/recallWebhook/ 308 Permanent Redirect POST /api/recallWebhook/ 308 Permanent Redirect
Webhook请求端点URL
https://****-***-248-85-9.ngrok-free.app/api/recallWebhook/
解决方案
1. 核心原因
Next.js App Router的API路由默认会对末尾带斜杠的URL返回308重定向,指向不带斜杠的版本。你当前的请求URL末尾带/,触发了这个默认行为。
2. 快速修复:修改Webhook URL
直接在SVIX的Webhook配置中,把请求URL改为不带末尾斜杠的版本:https://****-***-248-85-9.ngrok-free.app/api/recallWebhook
3. 备选方案:配置Next.js重定向规则
如果无法修改SVIX配置,可在next.config.js中添加重定向规则,将带斜杠的请求转发到目标路由:
/** @type {import('next').NextConfig} */ const nextConfig = { async redirects() { return [ { source: '/api/recallWebhook/', destination: '/api/recallWebhook', permanent: false, // 用临时重定向避免浏览器缓存 }, ] }, } module.exports = nextConfig
4. 先实现日志功能(跳过签名验证)
如果优先需要确认日志功能正常,可以暂时注释签名验证逻辑,确保请求能进入POST函数并打印日志:
import crypto from "crypto"; export async function POST(request) { try { const body = await request.json(); console.log("Webhook received:", body); // 暂时注释签名验证,先测试日志功能 /* const signingSecret = process.env.DEV_RECALL_WEBHOOK_SIGNING_SECRET; console.log("Signing secret:", signingSecret); const svixId = request.headers.get("svix-id"); const svixTimestamp = request.headers.get("svix-timestamp"); const svixSignature = request.headers.get("svix-signature"); const signedContent = `${svixId}.${svixTimestamp}.${JSON.stringify(body)}`; const secretBytes = Buffer.from(signingSecret.split("_")[1], "base64"); const computedSignature = crypto .createHmac("sha256", secretBytes) .update(signedContent) .digest("base64"); const isSignatureValid = svixSignature.split(" ").some((sig) => { const [version, signature] = sig.split(","); return signature === computedSignature; }); if (!isSignatureValid) { return new Response("Invalid signature", { status: 401 }); } */ console.log("Webhook logged successfully:", body); return new Response( JSON.stringify({ message: "Webhook received and logged successfully", }), { status: 200, headers: { "Content-Type": "application/json", }, } ); } catch (error) { console.error("Error processing webhook", error); return new Response( JSON.stringify({ message: "Error processing webhook" }), { status: 500, headers: { "Content-Type": "application/json", }, } ); } } export function GET() { return new Response("GET method not supported", { status: 405 }); }
内容的提问来源于stack exchange,提问作者maxwtd
相关产品推荐
相关产品推荐

