MedhaCloud
Link copied to clipboard!
Managed IT Support

IIS Logs: Location, Format & How to Read Them

Sreenivasa Reddy G
Sreenivasa Reddy G
Founder & CEO
Aug 3, 202610 min read
24
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 sites prints 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:

FieldMeaning
dateDate of the request (UTC).
timeTime of the request (UTC). Not server local time — a common off-by-timezone mistake when correlating with Event Viewer.
s-sitenameSite instance that served the request (W3SVC plus site ID).
s-computernameServer name.
s-ip / s-portServer IP address and port that received the request.
cs-methodHTTP verb (GET, POST, and so on).
cs-uri-stemThe requested path, without the query string.
cs-uri-queryThe query string, if any. Classic ASP also writes its error detail here on 500s.
cs-usernameAuthenticated user; hyphen for anonymous.
c-ipClient 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-hostHost header — which hostname the client asked for.
sc-statusHTTP status code returned.
sc-substatusIIS substatus code — the specific cause within the status class.
sc-win32-statusWindows error code for the request; 0 means success. Decode with net helpmsg.
sc-bytes / cs-bytesBytes sent by the server / received from the client. Off by default; worth enabling for bandwidth and upload analysis.
time-takenRequest 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

CodeMeaning
401.1Logon failed — invalid username or password.
401.2Logon failed due to server configuration — the authentication scheme the client used is not enabled.
401.3Unauthorized due to ACL on resource — NTFS permissions, not IIS configuration.
401.501 / 401.502Dynamic IP Restriction rate limits hit — concurrent or total request rate from one client IP.
401.503 / 401.504Client IP or host name is on the deny list.

404.x — not found (often request filtering, not a missing file)

CodeMeaning
404.0The file does not exist. The only 404 that means what browsers say it means.
404.2ISAPI or CGI restriction — the handler binary is not on the allow list.
404.3MIME type restriction — the extension has no MIME mapping, so the static file handler refused it.
404.4No handler configured for the extension.
404.5–404.15Request 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

CodeMeaning
500.0Generic application error — the code threw. Details are in the application's own logging and the Event Viewer, not in the IIS log.
500.19Configuration 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.21Handler has a bad module in its module list — typically ASP.NET not registered in IIS.
503.0Application 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.2Concurrent request limit (appConcurrentRequestLimit) exceeded.
503.3ASP.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.

IIS throwing errors right now? IIS web server support — log-driven diagnosis by an engineer, not a script. Live chat is open 24/7.

Topics

iis-logsiiswindows-server
Sreenivasa Reddy G
Written by

Sreenivasa Reddy G

Founder & CEO15+ 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.

Managed IT SupportCloud InfrastructureDigital Transformation
Follow on LinkedIn

Need Expert Help?

Our certified cloud and IT engineers are ready to tackle your toughest challenges — from migrations to managed services.