IIS

Regla de URL Rewrite con ARR en el web.config de tu sitio de IIS.

Markdown

En IIS, una regla de URL Rewrite reconoce a los bots y, con Application Request Routing (ARR) como proxy, les sirve la página de Jazui. Si tu aplicación es ASP.NET Core, es más sencillo y robusto usar el paquete de ASP.NET Core.

Antes de empezar

  • Los módulos URL Rewrite y Application Request Routing instalados (desde la web de IIS de Microsoft).
  • Tu sitio verificado antes de activar la regla: con ARR, si Jazui responde con error, el bot recibe ese error.
  • Tu token.

Instalación

Abre una consola como administrador y ejecuta estos dos comandos. El primero activa el proxy de ARR; el segundo permite que la regla escriba las cabeceras que necesita Jazui:

cmd
%windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/proxy /enabled:"True" /commit:apphost

%windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/rewrite/allowedServerVariables /+"[name='HTTP_AUTHORIZATION']" /+"[name='HTTP_X_JAZUI_URL']" /+"[name='HTTP_X_JAZUI_CLIENT_IP']" /+"[name='HTTP_COOKIE']" /commit:apphost

Después pon este web.config en la carpeta de tu sitio (o copia sus dos reglas en el que ya tengas, la de Jazui antes que la de la SPA):

web.configDescargar
<?xml version="1.0" encoding="utf-8"?>
<!--
  Jazui para IIS (SPA estática): los bots reciben el HTML renderizado; el resto, la SPA.
  Requiere URL Rewrite y Application Request Routing con el proxy activado, y permitir las variables del servidor
  (ver README.md). 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).
-->
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Jazui" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll">
            <!-- Un bot de la lista o _escaped_fragment_ en la query (el user-agent y la query separados por #) -->
            <!-- BOTS:BEGIN -->
            <add input="{HTTP_USER_AGENT}#{QUERY_STRING}" pattern="^[^#]*(?: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)|#.*_escaped_fragment_" />
            <!-- BOTS:END -->
            <add input="{REQUEST_METHOD}" pattern="^(GET|HEAD)$" />
            <add input="{HTTP_X_JAZUI}" pattern=".+" negate="true" />
            <!-- EXTENSIONS:BEGIN -->
            <add input="{URL}" pattern="\.(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)$" negate="true" />
            <!-- EXTENSIONS:END -->
          </conditions>
          <serverVariables>
            <set name="HTTP_AUTHORIZATION" value="Bearer [JAZUI_TOKEN]" />
            <!-- Si el sitio se sirve por http, cambia el esquema -->
            <set name="HTTP_X_JAZUI_URL" value="https://{HTTP_HOST}{UNENCODED_URL}" />
            <set name="HTTP_X_JAZUI_CLIENT_IP" value="{REMOTE_ADDR}" />
            <!-- Las cookies del visitante no le hacen falta a Jazui: no se envían -->
            <set name="HTTP_COOKIE" value="" />
          </serverVariables>
          <action type="Rewrite" url="https://api.jazui.com/Render" appendQueryString="false" />
        </rule>
        <!-- Fallback de la SPA: todo lo que no es un archivo va a index.html -->
        <rule name="SPA" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/index.html" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Cambia [JAZUI_TOKEN] por tu token. La dirección que se envía a Jazui empieza por https://: si tu sitio va por http, cámbialo en HTTP_X_JAZUI_URL.

Qué hace

  • La regla Jazui comprueba en un solo patrón el agente de usuario y _escaped_fragment_, el método, la cabecera X-Jazui y que la ruta no sea un archivo.
  • Escribe Authorization, X-Jazui-Url y X-Jazui-Client-IP como variables del servidor y reescribe la petición a https://api.jazui.com/Render, que ARR resuelve como proxy.
  • La regla SPA manda a index.html todo lo que no es un archivo ni una carpeta.

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.50 o «variable de servidor no permitida»: falta el segundo comando (las variables permitidas).
  • Error 404 o 502.3 al pedir como bot: el proxy de ARR no está activado (primer comando) o el servidor no puede salir a Internet por el puerto 443.
  • Detrás de Cloudflare, {REMOTE_ADDR} es la IP del CDN: usa {HTTP_CF_CONNECTING_IP} (o {HTTP_X_FORWARDED_FOR}) en HTTP_X_JAZUI_CLIENT_IP.
  • El tiempo máximo de espera es el del proxy de ARR (en la configuración del servidor, Application Request Routing Cache › Server Proxy Settings › Time-out); 20 segundos es un buen valor.