1// middleware.ts (Next.js App Router)
2import { NextRequest, NextResponse } from "next/server";
3
4const BOT_PATTERN = /(googlebot|bingbot|duckduckbot|gptbot|chatgpt-user|google-extended|perplexitybot|anthropic-ai|claudebot|cohere-ai)/i;
5
6export async function middleware(request: NextRequest) {
7 const ua = request.headers.get("user-agent") ?? "";
8 if (!BOT_PATTERN.test(ua)) return NextResponse.next();
9
10 const host = request.headers.get("x-forwarded-host") || request.headers.get("host") || "";
11 const proto = request.headers.get("x-forwarded-proto") || "https";
12 const pathWithQuery = request.nextUrl.pathname + request.nextUrl.search;
13 const canonicalUrl = host ? `${proto}://${host}${pathWithQuery}` : request.nextUrl.href;
14 const edgeUrl = `https://bot.seorender.io/edge/${encodeURIComponent(canonicalUrl)}`;
15
16 const headers: Record<string, string> = { "X-Original-User-Agent": ua, "User-Agent": ua };
17 headers["X-SeoRender-Token"] = process.env.BOT_EDGE_TOKEN ?? process.env.SEORENDER_TOKEN ?? "YOUR_TOKEN";
18
19 for (let attempt = 0; attempt < 3; attempt++) {
20 const res = await fetch(edgeUrl, { headers });
21 if (res.status === 200) {
22 const html = await res.text();
23 return new NextResponse(html, { status: 200, headers: { "content-type": "text/html; charset=utf-8" } });
24 }
25 if (res.status !== 202 || attempt === 2) break;
26 await new Promise((r) => setTimeout(r, 1500));
27 }
28 return NextResponse.next();
29}
30
31export const config = {
32 matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
33};