IIS Logs: Location, Format & How to Read Them


Our IIS server support team opens the IIS log before anything else in a web incident — the log states what happened, when, to which URL, with which status code, and how long it took. This page documents where the logs are, what each W3C field means, how to decode status and substatus codes, and the queries that turn a 200 MB log file into an answer.
Where IIS logs are stored
The default log directory is %SystemDrive%\inetpub\logs\LogFiles, per the Configure Logging in IIS documentation. Each site writes into its own subfolder named after the site ID:
C:\inetpub\logs\LogFiles\W3SVC1\u_ex260803.log
C:\inetpub\logs\LogFiles\W3SVC2\u_ex260803.log
W3SVC1 is site ID 1 (usually Default Web Site), W3SVC2 is site ID 2, and so on. The file name encodes the rollover date: u_ex260803.log is August 3, 2026, in yymmdd. Three ways to find a site's ID:
- IIS Manager: click Sites — the ID column is in the list view.
- Command line:
%windir%\system32\inetsrv\appcmd list sitesprints each site with its ID. - PowerShell:
Get-Website | Select-Object name, id.
The directory is configurable per site (Logging feature in IIS Manager, or the logFile element in applicationHost.config), so on a server someone else built, confirm the path before concluding logging is off. Note also that logging is handled by HTTP.sys and entries are flushed in batches — the current hour's traffic may not be on disk yet.
The W3C format and its fields
The default and recommended format is W3C Extended: space-separated ASCII, one request per line, timestamps in UTC, a hyphen for any empty field. Every file starts with header lines, and the #Fields: header defines the column order for the lines that follow — column positions are not fixed, they are whatever that header says. The fields, per the official field list:
| Field | Meaning |
|---|---|
date | Date of the request (UTC). |
time | Time of the request (UTC). Not server local time — a common off-by-timezone mistake when correlating with Event Viewer. |
s-sitename | Site instance that served the request (W3SVC plus site ID). |
s-computername | Server name. |
s-ip / s-port | Server IP address and port that received the request. |
cs-method | HTTP verb (GET, POST, and so on). |
cs-uri-stem | The requested path, without the query string. |
cs-uri-query | The query string, if any. Classic ASP also writes its error detail here on 500s. |
cs-username | Authenticated user; hyphen for anonymous. |
c-ip | Client IP address. Behind a load balancer or CDN this is the proxy's address unless X-Forwarded-For logging is configured. |
cs(User-Agent) | Client browser string. |
cs(Referer) | Referring URL. |
cs-host | Host header — which hostname the client asked for. |
sc-status | HTTP status code returned. |
sc-substatus | IIS substatus code — the specific cause within the status class. |
sc-win32-status | Windows error code for the request; 0 means success. Decode with net helpmsg. |
sc-bytes / cs-bytes | Bytes sent by the server / received from the client. Off by default; worth enabling for bandwidth and upload analysis. |
time-taken | Request duration in milliseconds. |
One quirk: any single field value longer than 4096 bytes is replaced with three dots (...) — by design, usually seen on oversized cookies.
Reading status and substatus codes
The status code gives the class of failure; the substatus gives the cause. A log line showing 401 2 and one showing 401 3 are different problems with different fixes. The full list is in the HTTP status codes in IIS reference; these are the ones that come up in real cases:
401.x — access denied
| Code | Meaning |
|---|---|
| 401.1 | Logon failed — invalid username or password. |
| 401.2 | Logon failed due to server configuration — the authentication scheme the client used is not enabled. |
| 401.3 | Unauthorized due to ACL on resource — NTFS permissions, not IIS configuration. |
| 401.501 / 401.502 | Dynamic IP Restriction rate limits hit — concurrent or total request rate from one client IP. |
| 401.503 / 401.504 | Client IP or host name is on the deny list. |
404.x — not found (often request filtering, not a missing file)
| Code | Meaning |
|---|---|
| 404.0 | The file does not exist. The only 404 that means what browsers say it means. |
| 404.2 | ISAPI or CGI restriction — the handler binary is not on the allow list. |
| 404.3 | MIME type restriction — the extension has no MIME mapping, so the static file handler refused it. |
| 404.4 | No handler configured for the extension. |
| 404.5–404.15 | Request Filtering rejections: blocked URL sequence (.5), denied verb (.6), denied extension (.7), hidden segment (.8), double escaping (.11), URL too long (.14), query string too long (.15). |
500.x and 503.x — server errors
| Code | Meaning |
|---|---|
| 500.0 | Generic application error — the code threw. Details are in the application's own logging and the Event Viewer, not in the IIS log. |
| 500.19 | Configuration data is invalid — malformed web.config, a locked section, missing module (URL Rewrite is the classic), or permissions on the config file. The HRESULT in the error page narrows it further. |
| 500.21 | Handler has a bad module in its module list — typically ASP.NET not registered in IIS. |
| 503.0 | Application pool unavailable — the pool is stopped or disabled. Check the System event log for the rapid-fail-protection or identity failure that stopped it. |
| 503.2 | Concurrent request limit (appConcurrentRequestLimit) exceeded. |
| 503.3 | ASP.NET queue full. |
Repeated 503.0 entries almost always trace back to the pool, not the site — see IIS application pool for the crash-and-restart mechanics behind it.
A sample line, decoded
#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-taken
2026-08-03 14:22:05 10.0.0.5 GET /app/orders.aspx id=4412 443 CONTOSO\jsmith 203.0.113.7 Mozilla/5.0+(Windows+NT+10.0) https://app.contoso.com/ 500 19 5 312
Read left to right against the #Fields header: at 14:22:05 UTC, server 10.0.0.5 received a GET for /app/orders.aspx with query id=4412 on port 443, from authenticated user CONTOSO\jsmith at client IP 203.0.113.7. The response was 500.19 — invalid configuration data — with Win32 status 5 (access denied; run net helpmsg 5 to decode), and the request took 312 ms. That combination points at permissions on web.config before anyone reads a line of application code.
time-taken: what it measures
The time-taken field is milliseconds from the moment HTTP.sys receives the first byte of the request until the last response send completes. Per the time-taken documentation, since IIS 7.0 this includes network time: HTTP.sys generally waits for the client to acknowledge the final response packet before recording the value. A large download to a client on a slow connection logs a huge time-taken even though the server did its work in milliseconds. The two exceptions where network time is excluded: responses of 2 KB or less served from memory, and applications that enable TCP buffering. So before treating a high time-taken as slow code, check sc-bytes — big response plus big time-taken usually means slow client, not slow server.
Analyzing logs: PowerShell and Log Parser
For a quick look at one file, PowerShell is enough. The field indexes below match the default #Fields order shown earlier — adjust them if your header differs.
# Top 10 requested URLs
$file = 'C:\inetpub\logs\LogFiles\W3SVC1\u_ex260803.log'
Get-Content $file | Where-Object { $_ -notmatch '^#' } |
ForEach-Object { ($_ -split ' ')[4] } |
Group-Object | Sort-Object Count -Descending |
Select-Object -First 10 Count, Name
# Status code distribution
Get-Content $file | Where-Object { $_ -notmatch '^#' } |
ForEach-Object { ($_ -split ' ')[11] } |
Group-Object | Sort-Object Count -Descending
For multi-gigabyte logs or multi-file queries, Log Parser 2.2 is still the standard tool. It is old (default install path is C:\Program Files (x86)\Log Parser 2.2) and no longer actively developed, but Microsoft's own IIS troubleshooting documentation is built around it, it understands the W3C format natively via -i:w3c, and it runs SQL over log files at millions of lines per second. Which URLs threw 500s, and how often:
logparser.exe "SELECT cs-uri-stem, COUNT(*) AS Hits
FROM C:\inetpub\logs\LogFiles\W3SVC1\u_ex*.log
WHERE sc-status = 500
GROUP BY cs-uri-stem ORDER BY Hits DESC" -i:w3c
The same pattern answers most questions: swap the WHERE clause for time-taken > 5000 to find slow requests, or group by c-ip on 404s to find scanners.
The other logs that matter
- HTTPERR —
%windir%\System32\LogFiles\HTTPERR. HTTP.sys logs kernel-level errors here: requests rejected before they ever reach a worker process, including connection drops, protocol violations, and 503s thrown when a pool's request queue is full. If a request is missing from the site log entirely, look here next. Reference: Error Logging in the HTTP Server API. - Event Viewer — the Application log holds ASP.NET unhandled-exception details behind a 500.0; the System log (source WAS) explains why an application pool stopped behind a 503.0.
- Failed Request Tracing (FREB) — per-request XML traces of the full IIS pipeline. Setup, per the failed request tracing guide: install the Tracing role service (Web Server > Health and Diagnostics > Tracing), then in IIS Manager select the site, click Failed Request Tracing in the Actions pane, enable it and set the log directory, then add a rule under Failed Request Tracing Rules with the status code to trap (for example 500, or a specific substatus like 404.2). Traces land as XML plus a viewer stylesheet in the configured directory. FREB is the tool for the case the flat log cannot answer: which module in the pipeline produced the status.
Log growth and cleanup
IIS never deletes its own logs. On a busy site the LogFiles tree grows until the disk fills, and a full system drive takes the sites down with it. Two controls:
- Rollover — per site, logs roll on a schedule (hourly, daily, weekly, monthly) or at a maximum file size (minimum 1,048,576 bytes). Daily is the default and fine for most servers; hourly keeps individual files manageable on high-traffic sites.
- Retention — there is no built-in retention setting. The standard pattern is a scheduled task running a deletion script:
# Delete IIS logs older than 30 days (run as a daily scheduled task)
Get-ChildItem 'C:\inetpub\logs\LogFiles' -Recurse -Filter *.log |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Remove-Item -Force
Pick the retention window to match how far back investigations actually reach — 30 to 90 days is typical, longer where compliance requires it (archive to cheaper storage rather than keeping live logs).
Disabling logging: the trade-off
Logging can be turned off per site, and on a high-volume site it saves measurable disk I/O and space. The cost is total: no traffic history, no error forensics, no scanner detection, no evidence after a security incident. The defensible middle ground is trimming the field list (drop Referer and User-Agent if nothing consumes them) and shortening retention — not disabling. Turn logging off only for sites whose traffic genuinely has no diagnostic value, such as a health-check endpoint hammered by a load balancer, and IIS supports that case better with a separate site or <httpLogging dontLog> on the specific URL than by blinding the whole server. If you are deciding what IIS itself does and does not record, what is IIS covers where logging sits in the request path.
Common investigations
- 500 spike — group 500s by cs-uri-stem and time bucket; read sc-substatus (500.19 is config, 500.0 is code); correlate the window with deployments and the Application event log.
- Slow pages — filter time-taken above threshold; check sc-bytes to separate slow clients from slow code; group survivors by URL and hand the top offenders to the developers with timestamps.
- Scanner noise — group 404s and 403s by c-ip; a single IP walking /wp-admin, /.env, and /phpmyadmin on a Windows server is a scanner; feed persistent offenders to Dynamic IP Restrictions or the firewall.
- Missing requests — request never appears in the site log: check HTTPERR (rejected in the kernel), then bindings (wrong site took it), then the log flush delay.
When to hand it over
Log analysis is cheap; acting on it at 2 a.m. is not. Our IIS support team reads these logs daily — 500-spike triage, FREB traces, pool crash diagnosis, and the cleanup automation that keeps the disk from filling — with an engineer on live chat 24/7.
Topics

Sreenivasa Reddy G
Founder & CEO • 15+ years
Sreenivasa Reddy is the Founder and CEO of Medha Cloud, recognized as "Startup of the Year 2024" by The CEO Magazine. With over 15 years of experience in cloud infrastructure and IT services, he leads the company's vision to deliver enterprise-grade cloud solutions to businesses worldwide.
More in Server Support
View all
What Is an Application Server? Types & Examples
8 min read

IIS Application Pool: Settings, Recycling & Crashes
9 min read

IIS URL Rewrite: Install, Rules & HTTPS Redirect
9 min read

IIS Manager: How to Open & Use It
9 min read

What Is IIS? Windows Web Server Setup, Sites & App Pools
9 min read

Windows Server Download: 2025 & 2022 Evaluation ISOs
8 min read