Apache

.htaccess para Apache con mod_rewrite y mod_proxy, también en muchos hostings compartidos.

Markdown

Con mod_rewrite, Apache reconoce a los bots y, con la bandera [P] de mod_proxy, les sirve la página de Jazui. El resto de peticiones van a tus archivos o a index.html.

Antes de empezar

  • Los módulos rewrite, headers, proxy, proxy_http y ssl activos, y SSLProxyEngine On en tu <VirtualHost>, con AllowOverride All para que se lea el .htaccess. En un hosting compartido, pregunta a tu proveedor si tiene mod_proxy.
  • Tu sitio verificado antes de activar la configuración: con el proxy de Apache, si Jazui responde con error, el bot recibe ese error.
  • Tu token.

En un servidor propio (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>

Instalación

Pon este .htaccess en la carpeta raíz de tu sitio (o sus reglas dentro de tu <VirtualHost>):

.htaccessDescargar
# 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>

Cambia [JAZUI_TOKEN] por tu token. Si ya tenías reglas para la SPA, deja las de Jazui antes.

Qué hace

  • Las condiciones comprueban que no haya cabecera X-Jazui, el método, el agente de usuario (o _escaped_fragment_) y que la ruta no sea un archivo.
  • La regla hace de proxy hacia https://api.jazui.com/Render y guarda en variables la dirección completa y la IP del bot.
  • mod_headers añade Authorization, X-Jazui-Url y X-Jazui-Client-IP solo a esas peticiones.
  • Las demás van a tus archivos o a index.html.

Mantener al día la lista de bots

Esta configuración lleva la lista de bots escrita dentro, entre las marcas BOTS:BEGIN y BOTS:END, porque el servidor no puede descargarla solo. Cuando Jazui añade bots nuevos:

  1. En el panel, ve a Sitios web y pulsa Token del sitio en cualquiera de tus sitios.
  2. En Bots en nginx, IIS y Apache, elige la pestaña de tu servidor y copia el bloque. Verás cuántos bots tiene y la fecha de la lista.
  3. Sustituye en tu configuración todo lo que hay entre BOTS:BEGIN y BOTS:END por ese bloque y recarga el servidor.

También puedes descargar la lista en texto con GET https://api.jazui.com/v2/bots.txt o los bloques ya hechos con GET https://api.jazui.com/v2/bots/config.

<p>Pide tu portada como si fueras un bot, con el agente de usuario de pruebas <code>integration-test</code>:</p><pre><code>curl -I -A "integration-test" https://misitio.com/</code></pre><p>La respuesta debe llevar <code>x-jazui: true</code> y <code>x-jazui-cache: MISS</code> la primera vez y <code>HIT</code> después. Sin agente de bot (<code>curl -I https://misitio.com/</code>) debe llegar tu web de siempre, sin <code>x-jazui</code>. En el panel, la petición aparece en <b>Tráfico de bots</b> y la página en <b>Páginas en caché</b>.</p>

Pide tu portada como si fueras un bot, con el agente de usuario de pruebas integration-test:

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

La respuesta debe llevar x-jazui: true y x-jazui-cache: MISS la primera vez y HIT después. Sin agente de bot (curl -I https://misitio.com/) debe llegar tu web de siempre, sin x-jazui. En el panel, la petición aparece en Tráfico de bots y la página en Páginas en caché.

Si algo no funciona

  • Error 500 al pedir como bot: falta algún módulo o SSLProxyEngine On; el registro de errores de Apache dice cuál.
  • No aparece x-jazui: comprueba que el .htaccess se lee (AllowOverride All).
  • Detrás de Cloudflare, REMOTE_ADDR es la IP del CDN: usa %{HTTP:CF-Connecting-IP} o mod_remoteip.
  • El tiempo máximo de espera es el del proxy de Apache (ProxyTimeout, 60 s por defecto); puedes bajarlo a 20 s en tu <VirtualHost>.