Next.js Integration
Next.js has built-in support for Middleware that runs at the edge before every request. This is the cleanest way to integrate SeoRend — no additional server needed.
How It Works
Section titled “How It Works”Bot → Next.js Middleware (edge) → SeoRend API → HTML responseUser → Next.js Middleware (edge) → Your Next.js AppThe middleware runs on Vercel’s Edge Network, Cloudflare, or any compatible runtime.
Requirements
Section titled “Requirements”- Next.js 13.1+
- SeoRend API key from app.seorend.com
-
Add your API key
Section titled “Add your API key”Add
SEOREND_API_KEYto your environment:.env.local SEOREND_API_KEY=sk_live_your_key_hereFor production, add it to your hosting provider’s environment variables (Vercel, Railway, etc.).
-
Create
Section titled “Create middleware.ts”middleware.tsPlace this file in the root of your project (same level as
package.json, not insidesrc/):middleware.ts import { NextRequest, NextResponse } from 'next/server';const BOT_PATTERNS = [// Google'googlebot', 'googleother', 'storebot-google', 'google-inspectiontool','google-extended', 'google-cloudvertexbot', 'google-favicon','apis-google', 'adsbot-google', 'mediapartners-google', 'feedfetcher-google', 'google page speed',// Bing / Microsoft'bingbot', 'bingpreview', 'msnbot', 'adidxbot', 'microsoftpreview',// Yandex'yandexbot', 'yandeximages', 'yandexvideo', 'yandexmobilebot', 'yandexmetrika', 'yandexaccessibilitybot', 'yabrowser',// Baidu & other search'baiduspider', 'duckduckbot', 'duckassistbot', 'applebot', 'naver','sogouspider', '360spider', 'coccoc', 'seznambot', 'qwantbot','ecosia', 'yahoo', 'exabot', 'petalbot', 'bravebot',// AI / LLM crawlers'gptbot', 'oai-searchbot', 'chatgpt-user', 'chatgpt','claudebot', 'claude-web', 'claude-user', 'claude-searchbot', 'anthropic-ai','perplexitybot', 'perplexity-user', 'amazonbot', 'ccbot','meta-externalagent', 'meta-externalfetcher','mistralai-user', 'cohere-ai', 'ai2bot','diffbot', 'deepseekbot', 'firecrawlagent', 'kagi-fetcher','bytespider', 'tiktokspider','xai-crawler', 'timpibot', 'anchor browser', 'novellum ai crawl', 'proratainc',// Social media / link previews'facebookexternalhit', 'facebookcatalog', 'facebookbot', 'facebookplatform', 'instagram','twitterbot', 'linkedinbot', 'pinterestbot', 'pinterest','slackbot', 'slack-imgproxy', 'discordbot', 'telegrambot', 'whatsapp','redditbot', 'snapchat', 'vkshare','tumblr', 'flipboard', 'outbrain', 'embedly', 'bitlybot', 'quora link preview', 'line', 'viber',// SEO tools'semrushbot', 'splitsignalbot', 'ahrefsbot', 'ahrefssiteaudit','mj12bot', 'rogerbot', 'dotbot', 'chrome-lighthouse', 'screaming frog','oncrawlbot', 'botifybot', 'deepcrawl', 'lumar', 'dataforseobot', 'serpstatbot', 'w3c_validator',// Internet Archive'ia_archiver', 'archive.org_bot',];const STATIC_EXT = /\.(js|css|png|jpg|jpeg|gif|ico|svg|webp|avif|woff2?|ttf|eot|pdf|zip|mp4|mp3|wasm|map)$/i;function isBot(userAgent: string): boolean {const ua = userAgent.toLowerCase();return BOT_PATTERNS.some(pattern => ua.includes(pattern));}export async function middleware(request: NextRequest) {const { pathname } = request.nextUrl;const ua = request.headers.get('user-agent') ?? '';// Skip: non-GET, static assets, Next.js internals, API routesif (request.method !== 'GET' ||STATIC_EXT.test(pathname) ||pathname.startsWith('/_next/') ||pathname.startsWith('/api/') ||pathname.startsWith('/__nextjs') ||request.headers.get('x-seorend-processed') === '1') {return NextResponse.next();}if (!isBot(ua)) {return NextResponse.next();}const apiKey = process.env.SEOREND_API_KEY;if (!apiKey) {console.error('[SeoRend] SEOREND_API_KEY is not set');return NextResponse.next();}try {const renderResponse = await fetch('https://render.seorend.com/v1/render', {method: 'POST',headers: {'Content-Type': 'application/json','Authorization': `Bearer ${apiKey}`,},body: JSON.stringify({url: request.url,userAgent: ua,}),});if (!renderResponse.ok) {// On 4xx/5xx errors, serve normally — site keeps workingreturn NextResponse.next();}const data = await renderResponse.json();return new NextResponse(data.html, {status: data.status ?? 200,headers: {'Content-Type': 'text/html; charset=utf-8','X-Seorend-Cache': data.cache?.hit ? 'HIT' : 'MISS','X-Seorend-Render-Time': String(data.timings?.total_ms ?? 0),},});} catch (err) {console.error('[SeoRend] Render error:', err);return NextResponse.next();}}// Apply middleware to all routes except static filesexport const config = {matcher: ['/((?!_next/static|_next/image|favicon|robots.txt|sitemap).*)',],}; -
Test locally
Section titled “Test locally”Terminal window npm run devThen test bot detection:
Terminal window curl -s -I -A "Googlebot/2.1" http://localhost:3000/# Should see: X-Seorend-Cache: MISS (or HIT)curl -s -I -A "Mozilla/5.0 Chrome/120" http://localhost:3000/# Should NOT have X-Seorend-* headers -
Deploy
Section titled “Deploy”Terminal window # Vercelvercel --prod# Or push to your connected Git branchgit push origin mainMake sure
SEOREND_API_KEYis set in your deployment environment.
Works With
Section titled “Works With”SeoRend middleware integrates seamlessly with:
- Static site generation (SSG)
- Server-side rendering (SSR)
- Incremental Static Regeneration (ISR)
- Client-side rendering (CSR) — this is the primary use case for SeoRend
Troubleshooting
Section titled “Troubleshooting”Middleware not running on Vercel: Check that middleware.ts is at the project root, not inside src/. Vercel looks for it at the root level.
process.env.SEOREND_API_KEY is undefined at runtime: Edge Middleware uses the Edge Runtime which does have access to environment variables — make sure the variable is added to Vercel’s Environment Variables settings (not just .env.local).
Infinite loop: The x-seorend-processed header check prevents loops, but if you have a reverse proxy in front, ensure it doesn’t strip custom headers.
Next Steps
Section titled “Next Steps”- Vercel integration — if you want Vercel-specific edge middleware
- Testing guide — verify your integration end-to-end
- Warm Bot — pre-warm important pages