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:
%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:apphostDespué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):
<?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 cabeceraX-Jazuiy que la ruta no sea un archivo. - Escribe
Authorization,X-Jazui-UrlyX-Jazui-Client-IPcomo variables del servidor y reescribe la petición ahttps://api.jazui.com/Render, que ARR resuelve como proxy. - La regla SPA manda a
index.htmltodo 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:
- En el panel, ve a Sitios web y pulsa Token del sitio en cualquiera de tus sitios.
- 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.
- Sustituye en tu configuración todo lo que hay entre
BOTS:BEGINyBOTS:ENDpor 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}) enHTTP_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.