Qué vas a conseguir
Leer un log de IIS para responder a "¿va lento?" con números, saber cuándo y por qué se recicla o se apaga el proceso de una aplicación, dejar la aplicación caliente antes de que llegue el primer usuario y redirigir todo el tráfico HTTP a HTTPS. Con eso se cierra uno de los tickets más repetidos en IIS: la aplicación tarda muchísimo la primera vez cada mañana y luego va bien.
Antes de empezar
- Windows Server 2022 o 2025 con IIS 10 y permisos de administrador local.
- Las características que usa esta guía:
Install-WindowsFeature Web-Http-Tracing, Web-AppInit, Web-Scripting-Tools. - El módulo URL Rewrite 2.1, que no es una característica de Windows: se descarga de la web de IIS y se instala aparte.
- El ID del sitio (
Get-IISSite): la carpeta de logs esW3SVC<ID>. Aquí, el sitioReservascon ID 2. La configuración básica de sitios, enlaces y certificados está en la primera guía de IIS.
1. Dónde están los logs y qué dice cada campo
Los logs de cada sitio están en %SystemDrive%\inetpub\logs\LogFiles\W3SVC2\, un fichero u_exAAMMDD.log por día. Las cuatro primeras líneas empiezan por # y la cuarta dice qué columnas hay:
#Software: Microsoft Internet Information Services 10.0#Version: 1.0#Date: 2026-11-02 00:00:01#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken2026-11-02 07:58:12 10.10.40.15 GET / - 443 CONTOSO\lmartin 10.20.4.37 Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64) - 200 0 0 218742026-11-02 07:58:34 10.10.40.15 GET /css/site.css - 443 CONTOSO\lmartin 10.20.4.37 Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64) https://reservas.corp.example/ 200 0 0 152026-11-02 07:58:41 10.10.40.15 POST /api/reservas - 443 CONTOSO\lmartin 10.20.4.37 Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64) - 500 0 0 312| Campo | Qué es |
|---|---|
date, time | Fecha y hora en UTC: 07:58 es 08:58 en Madrid en invierno |
cs-uri-stem | La página o recurso pedido |
cs-username | El usuario, si el sitio usa autenticación de Windows; - si es anónimo |
c-ip | La IP del cliente, o la del balanceador si hay uno delante |
sc-status, sc-substatus | El código HTTP y el subcódigo de IIS (un 401 2, un 404 0) |
sc-win32-status | El error de Windows; 64 o 1236 suelen ser el cliente que cerró la conexión |
time-taken | Milisegundos desde que llega la petición hasta que se envía el último byte |
- IIS escribe el log con un búfer de unos segundos: si no ves la última petición,
netsh http flush logbufferlo vuelca. - Con un balanceador delante, añade en Logging › Select Fields › Add Field un campo personalizado de tipo Request Header con
X-Forwarded-For.
2. Buscar las peticiones lentas con PowerShell
$log = "C:\inetpub\logs\LogFiles\W3SVC2\u_ex261102.log"$campos = ((Get-Content $log -TotalCount 4)[3] -replace '^#Fields: ').Split(' ')Get-Content $log | Where-Object { $_ -notlike '#*' } | ConvertFrom-Csv -Delimiter ' ' -Header $campos | Where-Object { [int]$_.'time-taken' -gt 5000 } | Select-Object time, cs-uri-stem, sc-status, time-takentime cs-uri-stem sc-status time-taken---- ----------- --------- ----------07:58:12 / 200 2187406:40:03 / 200 19240- Si en el mismo día IIS reinicia el sitio o cambia la configuración del log, aparece otra línea
#Fieldsa mitad de fichero. El filtro#*la salta. - Para agrupar (peticiones por código, tiempo medio por página),
Group-Object sc-statusoMeasure-Object time-taken -Averagesobre el mismo resultado. - Un
time-takenalto en ficheros estáticos (.css,.js) suele ser red o cliente lento; en la página principal o en la API, la aplicación.
3. Failed Request Tracing por tiempo
La primera guía de IIS usa FREB para los 500. Aquí sirve para lo contrario: peticiones que funcionan, pero tardan.
- Actívalo en el sitio (Actions › Failed Request Tracing... › Enable) y espera a la próxima petición lenta.
- El fichero de
%SystemDrive%\inetpub\logs\FailedReqLogFiles\W3SVC2\, abierto en el navegador, tiene la pestaña Performance View: qué módulo se ha llevado el tiempo. Si casi todo esManagedPipelineHandleroAspNetCoreModuleV2, el tiempo es de la aplicación, no de IIS. - Desactívalo al acabar: escribe un fichero por cada petición que cumple la regla.
4. Reciclado y tiempo de inactividad de los grupos de aplicaciones
Dos ajustes de cada grupo de aplicaciones, en Advanced Settings, deciden cuándo muere el proceso w3wp.exe:
| Ajuste | Por defecto | Qué hace |
|---|---|---|
| Process Model › Idle Time-out (minutes) | 20 | Sin peticiones durante ese tiempo, el proceso se apaga |
| Process Model › Idle Time-out Action | Terminate | Terminate lo cierra; Suspend lo deja en memoria congelado |
| Recycling › Regular Time Interval (minutes) | 1740 | Recicla cada 29 horas, a una hora distinta cada día |
| Recycling › Specific Times | (vacío) | Recicla a horas fijas |
| General › Start Mode | OnDemand | El proceso solo arranca con la primera petición |
- Reciclar es arrancar un proceso nuevo y retirar el viejo. Con el reciclado solapado (por defecto) el nuevo atiende mientras el viejo termina, pero llega en frío: la aplicación vuelve a cargar, compilar y llenar cachés.
- Activa el registro: Recycling › Generate Recycle Event Log Entry, con al menos Regular time interval, Specific time, Memory y Configuration changes. WAS lo deja en el registro Sistema: el evento 5074 es un reciclado por tiempo y el 5186, un proceso apagado por inactividad.
Un grupo de una aplicación de uso diario se deja así:
cd %windir%\system32\inetsrvappcmd set apppool "Reservas" /recycling.periodicRestart.time:00:00:00appcmd set apppool "Reservas" /+recycling.periodicRestart.schedule.[value='04:00:00']appcmd set apppool "Reservas" /processModel.idleTimeout:00:00:00appcmd set apppool "Reservas" /startMode:AlwaysRunningappcmd list apppool "Reservas" /text:*- Sin reciclado por intervalo y con uno fijo a las 4:00, el corte pasa cuando nadie trabaja.
idleTimeouta cero: el proceso no se apaga por la noche. En un servidor con decenas de grupos que casi no se usan, mejor Suspend que cero: menos memoria y un arranque mucho más rápido que en frío.AlwaysRunningarranca el proceso con el servicio WAS, sin esperar a la primera petición. Solo arranca el proceso: cargar la aplicación es el apartado siguiente.
5. Precarga con Application Initialization
Con la característica Web-AppInit instalada, IIS puede llamar a una página de la aplicación en cuanto arranca o se recicla el proceso, antes de enviarle tráfico de usuarios:
appcmd set site "Reservas" /applicationDefaults.preloadEnabled:trueY en el web.config de la aplicación, la página que la despierta:
<applicationInitialization doAppInitAfterRestart="true"> <add initializationPage="/health" /></applicationInitialization>- La página tiene que hacer el trabajo caro de verdad (abrir la conexión a SQL Server, cargar la configuración) y no pedir autenticación: si devuelve un 401, la precarga no calienta nada.
doAppInitAfterRestartrepite la precarga tras cada reciclado, no solo tras arrancar el servidor.- Funciona igual con ASP.NET 4.x y con ASP.NET Core en IIS. El cambio en
web.configes del equipo de la aplicación: acuérdalo con ellos y que vaya en su despliegue, o lo perderán en el siguiente.
6. Redirigir HTTP a HTTPS con URL Rewrite
El sitio necesita los dos enlaces: el 443 con su certificado y el 80, que solo existe para redirigir. Con URL Rewrite, la regla va en el web.config del sitio:
<rewrite> <rules> <rule name="HTTP a HTTPS" stopProcessing="true"> <match url="(.*)" /> <conditions> <add input="{HTTPS}" pattern="^OFF$" /> </conditions> <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" /> </rule> </rules></rewrite>- También se crea desde el Administrador de IIS: URL Rewrite › Add Rule(s)... › Blank rule, con los mismos campos.
redirectType="Permanent"es un 301: el navegador lo recuerda. Pruébalo primero conFound(302) y cámbialo cuando funcione.- Si hay un balanceador que termina el TLS y habla con IIS por HTTP,
{HTTPS}siempre valeOFFy la regla crea un bucle. En ese caso la condición mira la cabecera que pone el balanceador,{HTTP_X_FORWARDED_PROTO}con el patrón^http$. - En IIS 10 (desde la versión 1709) el sitio tiene además ajustes propios de HSTS, con la opción de redirigir a HTTPS sin URL Rewrite (
hsts.enabledyhsts.redirectHttpToHttpsen la configuración del sitio).
7. El ticket: "la aplicación va lentísima cada mañana con la primera petición"
Lunes, 9:05. Recepción dice que la aplicación de reservas tarda "como 20 segundos" en abrir a primera hora; después va bien todo el día. Algunos días pasa también después de comer.
- Medirlo: el script del apartado 2 sobre el log de hoy da
GET /contime-taken21874 a las 07:58 UTC (08:58 en Madrid) y, el viernes, otro de 19240 a las 14:12 UTC. Las peticiones siguientes, por debajo de 300 ms. - Qué pasó antes: en el Visor de eventos › Registros de Windows › Sistema, origen WAS:
Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Microsoft-Windows-WAS'; Id=5074,5186; StartTime=(Get-Date).AddDays(-3)} | Where-Object Message -like "*Reservas*" | Select-Object TimeCreated, Id, Message | Format-ListTimeCreated : 02/11/2026 7:12:44Id : 5186Message : A worker process with process id of '6412' serving application pool 'Reservas' was shutdown due to inactivity. Application Pool timeout configuration was set to 20 minutes. A new worker process will be started when needed.TimeCreated : 01/11/2026 13:51:20Id : 5074Message : A worker process with process id of '5120' serving application pool 'Reservas' has requested a recycle because the worker process reached its allowed processing time limit.- La causa: dos motivos con el mismo síntoma. Por la noche, tras 20 minutos sin uso, el proceso se apaga (5186) y el primer usuario de la mañana paga el arranque en frío. Y cada 29 horas el reciclado por intervalo (5074) cae a una hora distinta, a veces en plena jornada: de ahí "algunos días después de comer".
- El cambio, en una ventana acordada: reciclado fijo a las 4:00,
idleTimeouta cero,AlwaysRunning, precarga del sitio y la página/healthenapplicationInitialization(apartados 4 y 5).appcmdaplica el cambio y recicla el grupo: hazlo fuera de horas. - Comprobarlo a la mañana siguiente: en el log, la primera petición del día por debajo de un segundo, y en el registro de la aplicación las llamadas a
/healthjusto después de las 4:00 con el usuario vacío, que son la precarga.
Get-Content $log | Where-Object { $_ -notlike '#*' } | ConvertFrom-Csv -Delimiter ' ' -Header $campos | Where-Object 'cs-uri-stem' -eq '/' | Select-Object -First 1 time, cs-username, time-takentime cs-username time-taken---- ----------- ----------07:41:09 CONTOSO\mgarcia 412Quitar el reciclado no arregla una aplicación que pierde memoria: solo cambia cuándo se nota. Si el grupo crece día a día, pon un límite en Recycling › Private Memory Limit (KB) y abre un ticket al equipo de la aplicación con la gráfica de memoria.
En el ticket: tiempos antes y después, los eventos 5186 y 5074, los ajustes cambiados en el grupo y el sitio, y quién tiene el cambio de web.config en su despliegue.
Siguientes pasos
- La primera guía de IIS, sitios, enlaces, grupos de aplicaciones, certificados y un 503, cubre la base y el grupo detenido.
- La guía de Windows Server explica los roles, los parches y cómo leer el Visor de eventos.
- Para avisar antes que el usuario de que un sitio tarda, la guía de Zabbix: servidor, agentes y plantillas monta la monitorización con el agente de Windows.
¿Falta algo o en tu entorno es distinto? Escríbeme y lo corrijo.
Tech Career Lab · newsletter
¿Te ha servido? Cada dos semanas envío una idea práctica para perfiles IT: LinkedIn, proyectos, networking y procesos de selección.
Apúntate y llévate las 33 ideas →