Apache

.htaccess for Apache with mod_rewrite and mod_proxy, also on many shared hostings.

Markdown

With mod_rewrite, Apache recognizes bots and, with mod_proxy's [P] flag, serves them Jazui's page. Everything else goes to your files or to index.html.

Before you start

  • The rewrite, headers, proxy, proxy_http and ssl modules enabled, and SSLProxyEngine On in your <VirtualHost>, with AllowOverride All so the .htaccess is read. On shared hosting, ask your provider whether mod_proxy is available.
  • Your site verified before enabling the configuration: with Apache's proxy, if Jazui answers with an error, the bot gets that error.
  • Your token.

On your own server (Debian, Ubuntu):

Terminal
sudo a2enmod rewrite headers proxy proxy_http ssl
sudo systemctl restart apache2
VirtualHost
<VirtualHost *:443>
    ServerName misitio.com
    DocumentRoot /var/www/misitio
    SSLProxyEngine On
    <Directory /var/www/misitio>
        AllowOverride All
    </Directory>
</VirtualHost>

Installation

Put this .htaccess in your site's root folder (or its rules inside your <VirtualHost>):

.htaccessDownload
# Jazui para Apache (.htaccess o <VirtualHost>): los bots reciben el HTML renderizado; el resto, la SPA.
# Requiere mod_rewrite, mod_headers, mod_proxy, mod_proxy_http y SSLProxyEngine On en el VirtualHost.
# Sustituye [JAZUI_TOKEN] por el token de render del sitio. Las listas de bots y extensiones las rellena
# Sync-Integrations.ps1 (bots.txt, extensions.txt).

<IfModule mod_rewrite.c>
    RewriteEngine On

    <IfModule mod_proxy_http.c>
        # Nunca a sí mismo y solo GET/HEAD
        RewriteCond %{HTTP:X-Jazui} ^$
        RewriteCond %{REQUEST_METHOD} ^(GET|HEAD)$
        # Un bot de la lista o _escaped_fragment_ en la query
        # BOTS:BEGIN
        RewriteCond %{HTTP_USER_AGENT} (googlebot|google-inspectiontool|storebot-google|adsbot-google|bingbot|bingpreview|msnbot|adidxbot|yandex|baiduspider|duckduckbot|slurp|applebot|seznambot|qwantbot|qwantify|petalbot|sogou|naver|exabot|yetibot|360spider|soso|gptbot|chatgpt|oai-searchbot|claudebot|claude-user|claude-searchbot|anthropic-ai|perplexity|google-cloudvertexbot|bytespider|ccbot|amazonbot|meta-externalagent|cohere-ai|mistralai-user|youbot|duckassistbot|yibot|qwenbot|tongyibot|erniebot|yiyanbot|doubaobot|tiktokspider|deepseekbot|pangubot|manus-user|kimi-agent|kimi-searchbot|kimi-user|kimibot|moonshotbot|hunyuan|chatglm-spider|agi\ agent|ai2bot|ai2bot-deepresearcheval|ai2bot-dolma|aihitbot|amazon-kendra|amazon-qbusiness|amazonbuyforme|amzn-searchbot|amzn-user|bedrockbot|novaact|andibot|claude-web|apifybot|apifywebsitecontentcrawler|atlassian-bot|awario|bigsur\.ai|brightbot|buddybot|cartai\ agentic\ commerce|terracotta|cledarabot|cloudflare-autorag|cloudflarebrowserrenderingcrawler|cohere-training-data-crawler|cragcrawler|crawlspace|datenbank\ crawler|diffbot|diffbot-user|anomura|echobot\ bot|echoboxbot|elevenlabsbot|elevenlabscrawler|exasearchbot|exauser|factset_spyderbot|firecrawlagent|geisthaus-pagefetcher|cloudvertexbot|gemini-deep-research|google-agent|google-firebase|google-notebooklm|googleagent-mariner|googleagent-urlcontext|googleother|googleother-image|googleother-video|notebooklm|iaskbot|iaskspider|imagesiftbot|img2dataset|isscyberriskcrawler|kagi-fetcher|keenablebot|klaviyoaibot|laiondownloader|linguee\ bot|linkupbot|aiwebindex|meta-externalfetcher|meta-webindexer|facebookbot|azureai-searchbot|mistralai-index|mistralai-training|mozilla-tabstack|nagetbot|netestate\ imprint\ crawler|icc-crawler|oai-adsbot|panscient|panscient\.com|shapbot|payhawk\ invoice\ fetching\ agent|phindbot|poggio-citations|poseidon\ research\ crawler|qualifiedbot|wpbot|querit-searchbot|queritbot|quillbot|quillbot\.com|reflectionbot|cotoyogi|sbintuitionsbot|scrybot|sidetrade\ indexer\ bot|tavilybot|agenttimes|thinkbot|timpibot|twinagent|velenpublicwebcrawler|wardbot|omgili|omgilibot|grok\ bot|grokappandroid|grokbot|addsearchbot|agentdatabot|aranet-searchbot|bixelbot|bravebot|channel3bot|friendlycrawler|henkbot|imagespider|kangaroo\ bot|kunatocrawler|laion-huggingface-processor|linerbot|mycentralaiscraperbot|terra\ cotta|wrtnbot|zanistabot|facebookexternalhit|facebot|twitterbot|linkedinbot|slackbot|whatsapp|telegrambot|discordbot|pinterest|redditbot|skypeuripreview|embedly|quora\ link\ preview|showyoubot|outbrain|developers\.google\.com\/\+\/web\/snippet|vkshare|tumblr|bitlybot|nuzzel|flipboard|snapchat|viber|mattermost|bitrix\ link\ preview|xing-contenttabreceiver|ahrefsbot|semrushbot|mj12bot|majestic-12|dotbot|rogerbot|screaming\ frog|sitebulb|serpstatbot|seokicks|siteimprove|lighthouse|google\ page\ speed|gtmetrix|pingdom|uptimerobot|uptimebot|w3c_validator|w3c-checklink|archive\.org_bot|paperlibot|dataprovider\.com|grapeshotcrawler|zoominfobot|feedly|newsblur|inoreader|integration-test) [NC,OR]
        # BOTS:END
        RewriteCond %{QUERY_STRING} _escaped_fragment_
        # Archivos que nunca se renderizan
        # EXTENSIONS:BEGIN
        RewriteCond %{REQUEST_URI} !\.(js|mjs|css|less|map|json|xml|rss|txt|webmanifest|ico|png|jpg|jpeg|gif|webp|avif|svg|bmp|tif|tiff|psd|ai|woff|woff2|ttf|otf|eot|mp3|m4a|wav|ogg|mp4|m4v|webm|avi|mov|mpg|mpeg|wmv|flv|swf|pdf|doc|docx|xls|xlsx|ppt|pptx|csv|zip|gz|rar|7z|iso|torrent|exe|dmg|apk|wasm|dll|dat|blat)$ [NC]
        # EXTENSIONS:END
        # Ruta y query tal cual las pidió el bot (la URL completa viaja en X-Jazui-Url)
        RewriteCond %{THE_REQUEST} ^\S+\s(\S+)
        RewriteRule ^ https://api.jazui.com/Render [P,L,QSD,E=JAZUI_URL:%{REQUEST_SCHEME}://%{HTTP_HOST}%1,E=JAZUI_IP:%{REMOTE_ADDR}]
    </IfModule>

    # Fallback de la SPA: todo lo que no es un archivo va a index.html
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^ index.html [L]
</IfModule>

<IfModule mod_headers.c>
    RequestHeader set Authorization "Bearer [JAZUI_TOKEN]" env=JAZUI_URL
    RequestHeader set X-Jazui-Url "%{JAZUI_URL}e" env=JAZUI_URL
    # Detrás de un CDN usa su cabecera (CF-Connecting-IP, X-Forwarded-For)
    RequestHeader set X-Jazui-Client-IP "%{JAZUI_IP}e" env=JAZUI_URL
    # Las cookies del visitante no le hacen falta a Jazui: no se envían
    RequestHeader unset Cookie env=JAZUI_URL
</IfModule>

Replace [JAZUI_TOKEN] with your token. If you already had SPA rules, keep Jazui's before them.

What it does

  • The conditions check that there's no X-Jazui header, the method, the user agent (or _escaped_fragment_) and that the path isn't a file.
  • The rule proxies to https://api.jazui.com/Render and stores the full address and the bot's IP in variables.
  • mod_headers adds Authorization, X-Jazui-Url and X-Jazui-Client-IP only to those requests.
  • Everything else goes to your files or to index.html.

Keeping the bot list up to date

This configuration has the bot list written inside it, between the BOTS:BEGIN and BOTS:END markers, because the server can't download it on its own. When Jazui adds new bots:

  1. In the dashboard, go to Websites and click Site token on any of your sites.
  2. Under Bots in nginx, IIS and Apache, choose your server's tab and copy the block. You'll see how many bots it has and the list date.
  3. Replace everything between BOTS:BEGIN and BOTS:END in your configuration with that block and reload the server.

You can also download the list as text with GET https://api.jazui.com/v2/bots.txt or the ready-made blocks with GET https://api.jazui.com/v2/bots/config.

<p>Request your home page as if you were a bot, with the test user agent <code>integration-test</code>:</p><pre><code>curl -I -A "integration-test" https://mysite.com/</code></pre><p>The response must include <code>x-jazui: true</code>, with <code>x-jazui-cache: MISS</code> the first time and <code>HIT</code> afterwards. Without a bot user agent (<code>curl -I https://mysite.com/</code>) you should get your usual website, without <code>x-jazui</code>. In the dashboard, the request shows up in <b>Bot traffic</b> and the page in <b>Cached pages</b>.</p>

Request your home page as if you were a bot, with the test user agent integration-test:

curl -I -A "integration-test" https://mysite.com/

The response must include x-jazui: true, with x-jazui-cache: MISS the first time and HIT afterwards. Without a bot user agent (curl -I https://mysite.com/) you should get your usual website, without x-jazui. In the dashboard, the request shows up in Bot traffic and the page in Cached pages.

If something doesn't work

  • Error 500 when requesting as a bot: a module or SSLProxyEngine On is missing; Apache's error log says which.
  • No x-jazui: check that the .htaccess is read (AllowOverride All).
  • Behind Cloudflare, REMOTE_ADDR is the CDN's IP: use %{HTTP:CF-Connecting-IP} or mod_remoteip.
  • The maximum wait is Apache's proxy timeout (ProxyTimeout, 60 s by default); you can lower it to 20 s in your <VirtualHost>.