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.
What you'll get
Reading an IIS log to answer "is it slow?" with numbers, knowing when and why an application's process is recycled or shut down, keeping the application warm before the first user arrives, and redirecting all HTTP traffic to HTTPS. That closes one of the most repeated IIS tickets: the application takes ages the first time every morning and then runs fine.
Before you start
- Windows Server 2022 or 2025 with IIS 10 and local administrator rights.
- The features this guide uses:
Install-WindowsFeature Web-Http-Tracing, Web-AppInit, Web-Scripting-Tools. - The URL Rewrite 2.1 module, which is not a Windows feature: it is downloaded from the IIS website and installed separately.
- The site ID (
Get-IISSite): the log folder isW3SVC<ID>. Here, theReservassite with ID 2. The basic setup of sites, bindings and certificates is in the first IIS guide.
1. Where the logs are and what each field says
Each site's logs are in %SystemDrive%\inetpub\logs\LogFiles\W3SVC2\, one u_exYYMMDD.log file per day. The first four lines start with # and the fourth says which columns there are:
#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| Field | What it is |
|---|---|
date, time | Date and time in UTC: 07:58 is 08:58 in Madrid in winter |
cs-uri-stem | The page or resource requested |
cs-username | The user, if the site uses Windows authentication; - if anonymous |
c-ip | The client IP, or the load balancer's if there is one in front |
sc-status, sc-substatus | The HTTP status and the IIS substatus (a 401 2, a 404 0) |
sc-win32-status | The Windows error; 64 or 1236 are usually the client closing the connection |
time-taken | Milliseconds from the request arriving until the last byte is sent |
- IIS writes the log through a buffer of a few seconds: if you cannot see the latest request,
netsh http flush logbufferflushes it. - With a load balancer in front, add in Logging › Select Fields › Add Field a custom field of type Request Header with
X-Forwarded-For.
2. Finding slow requests with PowerShell
$log = "C:\inetpub\logs\LogFiles\W3SVC2\u_ex261102.log"$fields = ((Get-Content $log -TotalCount 4)[3] -replace '^#Fields: ').Split(' ')Get-Content $log | Where-Object { $_ -notlike '#*' } | ConvertFrom-Csv -Delimiter ' ' -Header $fields | 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- If IIS restarts the site or the log configuration changes during the day, another
#Fieldsline appears mid-file. The#*filter skips it. - To group (requests per status, average time per page),
Group-Object sc-statusorMeasure-Object time-taken -Averageon the same output. - A high
time-takenon static files (.css,.js) is usually a slow network or client; on the main page or the API, the application.
3. Failed Request Tracing by time
The first IIS guide uses FREB for 500s. Here it serves the opposite case: requests that work, but slowly.
- Enable it on the site (Actions › Failed Request Tracing... › Enable) and wait for the next slow request.
- The file in
%SystemDrive%\inetpub\logs\FailedReqLogFiles\W3SVC2\, opened in the browser, has a Performance View tab: which module took the time. If nearly all of it isManagedPipelineHandlerorAspNetCoreModuleV2, the time belongs to the application, not IIS. - Turn it off when you are done: it writes a file for every request that matches the rule.
4. Application pool recycling and idle time-out
Two settings of each application pool, in Advanced Settings, decide when the w3wp.exe process dies:
| Setting | Default | What it does |
|---|---|---|
| Process Model › Idle Time-out (minutes) | 20 | With no requests for that long, the process shuts down |
| Process Model › Idle Time-out Action | Terminate | Terminate closes it; Suspend keeps it frozen in memory |
| Recycling › Regular Time Interval (minutes) | 1740 | Recycles every 29 hours, at a different time each day |
| Recycling › Specific Times | (empty) | Recycles at fixed times |
| General › Start Mode | OnDemand | The process only starts with the first request |
- Recycling means starting a new process and retiring the old one. With overlapped recycling (the default) the new one serves while the old one finishes, but it arrives cold: the application loads, compiles and fills its caches again.
- Turn on logging: Recycling › Generate Recycle Event Log Entry, with at least Regular time interval, Specific time, Memory and Configuration changes. WAS writes to the System log: event 5074 is a time-based recycle and 5186, a process shut down for inactivity.
A pool for an application used every day is set like this:
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:*- With no interval recycling and a fixed one at 4:00, the cut happens when nobody is working.
idleTimeoutat zero: the process does not shut down at night. On a server with dozens of rarely used pools, Suspend is better than zero: less memory and a much faster start than a cold one.AlwaysRunningstarts the process with the WAS service, without waiting for the first request. It only starts the process: loading the application is the next section.
5. Preloading with Application Initialization
With the Web-AppInit feature installed, IIS can call a page of the application as soon as the process starts or recycles, before sending it user traffic:
appcmd set site "Reservas" /applicationDefaults.preloadEnabled:trueAnd in the application's web.config, the page that wakes it up:
<applicationInitialization doAppInitAfterRestart="true"> <add initializationPage="/health" /></applicationInitialization>- The page has to do the genuinely expensive work (open the SQL Server connection, load configuration) and must not require authentication: if it returns a 401, the preload warms nothing.
doAppInitAfterRestartrepeats the preload after every recycle, not only after the server starts.- It works the same with ASP.NET 4.x and with ASP.NET Core on IIS. The
web.configchange belongs to the application team: agree it with them and have it in their deployment, or they will lose it on the next one.
6. Redirecting HTTP to HTTPS with URL Rewrite
The site needs both bindings: 443 with its certificate and 80, which only exists to redirect. With URL Rewrite, the rule goes in the site's web.config:
<rewrite> <rules> <rule name="HTTP to 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>- It can also be created from IIS Manager: URL Rewrite › Add Rule(s)... › Blank rule, with the same fields.
redirectType="Permanent"is a 301: the browser remembers it. Test first withFound(302) and switch once it works.- If a load balancer terminates TLS and talks to IIS over HTTP,
{HTTPS}is alwaysOFFand the rule creates a loop. In that case the condition checks the header the balancer sets,{HTTP_X_FORWARDED_PROTO}with the pattern^http$. - In IIS 10 (from version 1709) the site also has its own HSTS settings, with the option to redirect to HTTPS without URL Rewrite (
hsts.enabledandhsts.redirectHttpToHttpsin the site configuration).
7. The ticket: "the application is very slow every morning at the first request"
Monday, 9:05. Reception says the bookings app takes "about 20 seconds" to open first thing; after that it is fine all day. Some days it also happens after lunch.
- Measure it: the script from section 2 on today's log gives
GET /withtime-taken21874 at 07:58 UTC (08:58 in Madrid) and, on Friday, another of 19240 at 14:12 UTC. The following requests, under 300 ms. - What happened before: in Event Viewer › Windows Logs › System, source 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.- The cause: two reasons with the same symptom. At night, after 20 minutes without use, the process shuts down (5186) and the first user of the morning pays for the cold start. And every 29 hours the interval recycle (5074) lands at a different time, sometimes mid-day: hence "some days after lunch".
- The change, in an agreed window: a fixed recycle at 4:00,
idleTimeoutat zero,AlwaysRunning, site preload and the/healthpage inapplicationInitialization(sections 4 and 5).appcmdapplies the change and recycles the pool: do it out of hours. - Check it the next morning: in the log, the first request of the day under one second, and the
/healthcalls just after 4:00 with an empty user, which are the preload.
Get-Content $log | Where-Object { $_ -notlike '#*' } | ConvertFrom-Csv -Delimiter ' ' -Header $fields | Where-Object 'cs-uri-stem' -eq '/' | Select-Object -First 1 time, cs-username, time-takentime cs-username time-taken---- ----------- ----------07:41:09 CONTOSO\mgarcia 412Removing recycling does not fix an application that leaks memory: it only changes when you notice. If the pool grows day after day, set a limit in Recycling › Private Memory Limit (KB) and open a ticket with the application team with the memory graph.
In the ticket: times before and after, events 5186 and 5074, the settings changed on the pool and the site, and who has the web.config change in their deployment.
Next steps
- The first IIS guide, sites, bindings, application pools, certificates and a 503, covers the basics and the stopped pool.
- The Windows Server guide explains roles, patching and how to read Event Viewer.
- To find out before the user that a site is slow, the Zabbix: server, agents and templates guide sets up monitoring with the Windows agent.
Is something missing, or different in your environment? Get in touch and I'll fix it.
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.Found this useful? Every two weeks I send one practical idea for IT professionals: LinkedIn, projects, networking and hiring processes.
Apúntate y llévate las 33 ideasSign up and get the 33 ideas →