Skip to content

Node.js / Express Integration

This guide covers integrating SeoRend into any Node.js server — Express, Fastify, Koa, or a plain http server. The same middleware logic works across all of them.

The middleware checks every incoming GET request:

  • Bot detected → forwards to SeoRend, returns pre-rendered HTML
  • Regular user → passes request to your app unchanged
  1. Terminal window
    npm install express
  2. Create seorend.middleware.js:

    seorend.middleware.js
    const SEOREND_API_URL = 'https://render.seorend.com/v1/render';
    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|wasm|map)$/i;
    function isBot(ua) {
    if (!ua) return false;
    const lower = ua.toLowerCase();
    return BOT_PATTERNS.some(p => lower.includes(p));
    }
    module.exports = function seorend(options = {}) {
    const apiKey = options.apiKey || process.env.SEOREND_API_KEY;
    if (!apiKey) throw new Error('SEOREND_API_KEY is required');
    return async function(req, res, next) {
    const ua = req.headers['user-agent'] || '';
    // Skip: non-GET, static files, loop guard
    if (req.method !== 'GET') return next();
    if (STATIC_EXT.test(req.path)) return next();
    if (req.headers['x-seorend-processed'] === '1') return next();
    if (!isBot(ua)) return next();
    const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`;
    try {
    const response = await fetch(SEOREND_API_URL, {
    method: 'POST',
    headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${apiKey}`,
    },
    body: JSON.stringify({ url, userAgent: ua }),
    });
    if (!response.ok) return next();
    const data = await response.json();
    res.set('Content-Type', 'text/html; charset=utf-8');
    res.set('X-Seorend-Cache', data.cache?.hit ? 'HIT' : 'MISS');
    return res.status(data.status || 200).send(data.html);
    } catch {
    return next();
    }
    };
    };
  3. server.js
    const express = require('express');
    const seorend = require('./seorend.middleware');
    const app = express();
    // Add SeoRend BEFORE your static files and routes
    app.use(seorend({ apiKey: process.env.SEOREND_API_KEY }));
    // Your existing middleware and routes
    app.use(express.static('public'));
    app.get('*', (req, res) => res.sendFile('index.html', { root: 'public' }));
    app.listen(3000, () => console.log('Server running on port 3000'));
Terminal window
# Linux / macOS
export SEOREND_API_KEY=sk_live_your_key_here
# In a .env file (with dotenv)
SEOREND_API_KEY=sk_live_your_key_here
# Run with env inline
SEOREND_API_KEY=sk_live_... node server.js
Terminal window
# Should return X-Seorend-Cache
curl -s -I -A "Googlebot/2.1" http://localhost:3000/
# Should NOT have X-Seorend headers
curl -s -I -A "Mozilla/5.0" http://localhost:3000/