Microsoft IIS#
Integration Details |
|
Ingester |
|
Ingester |
Microsoft IIS Configuration#
IIS writes one log file per site per day in the W3C Extended log file format, and HTTP.sys writes a separate error log (see HTTP.sys Error Log below). Gravwell’s Windows File Follower reads both on the IIS server. See Configure Logging in IIS and the <logFile> reference.
Log directory:
%SystemDrive%\inetpub\logs\LogFiles\W3SVC<site ID>\, one folder per site.File name:
u_ex<YYMMDD>.log, one file per day by default.Keep the log format W3C, the default.
Logging Fields#
W3C logs are space-delimited, and IIS writes only the fields you select, always in a fixed order. What a server logs today depends on its history:
If its field list has never been changed, it logs the 15 IIS default fields.
On Windows Server 2019 and later (and Windows 11), a Windows update from February 2026 or later adds Bytes Sent and Bytes Received to those defaults, for 17 fields.
If fields were chosen in IIS Manager (Select Fields) or by a script, it logs that selection.
Whichever applies, add the following fields to the defaults. IIS doesn’t log them by default (except Bytes Sent and Bytes Received after the update above), and Gravwell’s per-site, per-server, host name and bandwidth queries need them:
IIS Manager name |
|
Log field |
What it adds |
|---|---|---|---|
Site Name |
|
|
Which site served the request, as the site ID ( |
Server Name |
|
|
Which server, when logs from several servers are collected |
Protocol Version |
|
|
The HTTP version of the request |
Host |
|
|
The host name the client requested |
Bytes Sent |
|
|
Response size |
Bytes Received |
|
|
Request size, including uploads |
Warning
Enable all six fields, or none of them. Gravwell reads IIS logs by position. Some partial selections shift values into the wrong fields without any error: Bytes Sent without Bytes Received, for example, makes response sizes appear as response times. Don’t add other standard fields, such as Cookie, for the same reason.
The X-Forwarded-For field in Behind a Reverse Proxy is a custom field. IIS writes custom fields after the standard ones, so it doesn’t shift them. Don’t add other custom fields.
Set the Recommended Fields#
Run these PowerShell commands as an administrator on the IIS server. They set the same 21 fields whatever the server logs today.
Back up the IIS configuration. The backup name must be new: if a backup with that name already exists, the command fails and keeps the old one.
Import-Module WebAdministration Backup-WebConfiguration -Name 'before-gravwell-logging'
Record the current field lists. The first command prints the server-wide list. The second prints each site’s list in full.
Get-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' ` -Filter 'system.applicationHost/sites/siteDefaults/logFile' -Name logExtFileFlags Get-ChildItem IIS:\Sites | Select-Object Name, @{ n = 'logExtFileFlags'; e = { $_.logFile.logExtFileFlags } } | Format-List
Set the recommended fields as the server-wide list:
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' ` -Filter 'system.applicationHost/sites/siteDefaults/logFile' -Name logExtFileFlags ` -Value 'Date,Time,ClientIP,UserName,ServerIP,Method,UriStem,UriQuery,TimeTaken,HttpStatus,Win32Status,ServerPort,UserAgent,HttpSubStatus,Referer,SiteName,ComputerName,ProtocolVersion,Host,BytesSent,BytesRecv'
List the sites that log a different field list from the server-wide one:
$d = Get-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' ` -Filter 'system.applicationHost/sites/siteDefaults/logFile' -Name logExtFileFlags Get-ChildItem IIS:\Sites | Where-Object { $_.logFile.logExtFileFlags -ne $d } | Select-Object Name
No output means every site uses the server-wide list. A listed site has its own field list. Remove it so the site uses the server-wide list:
Clear-ItemProperty 'IIS:\Sites\<site name>' -Name logFile.logExtFileFlags
Browse each site once, then check the fields IIS writes to each site’s newest log file:
Get-ChildItem C:\inetpub\logs\LogFiles -Directory -Filter W3SVC* | ForEach-Object { $f = Get-ChildItem $_.FullName -Filter u_ex*.log | Sort-Object LastWriteTime | Select-Object -Last 1 [pscustomobject]@{ Site = $_.Name; Fields = (Select-String -Path $f.FullName -Pattern '^#Fields:' | Select-Object -Last 1).Line.Split(' ').Count - 1 } }
Every site should show
21(22onceX-Forwarded-Foris added). IIS writes the new field list only with a site’s next request, so a site that hasn’t been browsed still shows its old count. A folder for a site that no longer exists keeps its last count; ignore it. The command assumes the default log directory.
The server-wide list applies to every site that doesn’t have its own list, including sites created later. Step 3 replaces the whole list, and it’s safe to run again.
The change applies immediately, without restarting IIS.
In IIS Manager instead: select the server node, open Logging, choose Select Fields, and select the default fields plus the six above.
If the server is behind a reverse proxy or load balancer, also add
X-Forwarded-For(see Behind a Reverse Proxy).
The resulting layout is:
#Fields: date time s-sitename s-computername s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs-version cs(User-Agent) cs(Referer) cs-host sc-status sc-substatus sc-win32-status sc-bytes cs-bytes time-taken
To go back to the original field list, run step 3 with the list step 2 printed. On a server whose fields were never changed, this saves the default list as an explicit setting; the fields logged are the same. Or run:
Restore-WebConfiguration -Name 'before-gravwell-logging'
This restores the whole IIS configuration to the backup from step 1, including anything else changed since.
Behind a Reverse Proxy#
Behind a reverse proxy or load balancer, c-ip holds the proxy’s address for every request. To keep the client’s address, log the X-Forwarded-For request header as a custom field (IIS 8.5 and later). Run these commands once, after setting the recommended fields:
Back up the IIS configuration, using a new backup name:
Import-Module WebAdministration Backup-WebConfiguration -Name 'before-gravwell-xff'
List any custom fields the server already logs. No output means it logs none.
Get-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' ` -Filter 'system.applicationHost/sites/siteDefaults/logFile/customFields' -Name Collection | Select-Object logFieldName, sourceName, sourceType
Add
X-Forwarded-For:Add-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' ` -Filter 'system.applicationHost/sites/siteDefaults/logFile/customFields' -Name '.' ` -Value @{ logFieldName = 'X-Forwarded-For'; sourceName = 'X-Forwarded-For'; sourceType = 'RequestHeader' }
In IIS Manager: server node > Logging > Select Fields > Add Field, with Source type set to Request Header and Source set to X-Forwarded-For.
If step 3 reports
Cannot add duplicate collection entry, the field is already configured. Nothing needs to change.IIS writes custom fields after the standard fields, so
X-Forwarded-Foris the last field. Requests that don’t come through the proxy log-.c-ipalways stays the address IIS received the connection from.X-Forwarded-Foris set by the client or the proxy, so treat it as context, not proof of where a request came from.
To remove only X-Forwarded-For again (safe to run more than once):
Remove-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter 'system.applicationHost/sites/siteDefaults/logFile/customFields' -Name '.' `
-AtElement @{ logFieldName = 'X-Forwarded-For' }
Or run:
Restore-WebConfiguration -Name 'before-gravwell-xff'
This also rolls back every other IIS change made since the backup.
Note
Named backups stay in %windir%\System32\inetsrv\backup\<name>\ until you delete the folder, for example Remove-Item -Recurse "$env:windir\System32\inetsrv\backup\before-gravwell-logging". The backup list shows every creation time as midnight, so tell same-day backups apart by name.
Log Format#
dateandtimeare UTC, whatever the server’s time zone.IIS writes
+in place of spaces incs(User-Agent)andcs(Referer), and-for any empty field.time-takenis in milliseconds.s-sitenameis the site ID (W3SVC1), not the site’s name. IIS Manager lists each site’s ID under Sites.IIS writes header lines (
#Software,#Version,#Date,#Fields) at the start of every file, and again whenever IIS restarts or the logging settings change.
HTTP.sys Error Log (HTTPERR)#
HTTP.sys, the Windows component that receives requests before IIS, rejects some requests and connections itself, including some path traversal attempts. They never reach the IIS logs above and are written only to the HTTP.sys error log, so collect it as a second stream. See Error Logging in the HTTP Server API.
Error logging is on by default. Nothing needs to change in IIS or Windows.
Log directory:
%SystemRoot%\System32\LogFiles\HTTPERR\, one log per server, not per site.Files are named
httperr<N>.log. HTTP.sys starts the next file when the current one reaches 1 MB. Each file starts with its own header lines.
The layout is fixed by Windows:
#Fields: date time c-ip c-port s-ip s-port cs-version cs-method cs-uri streamid streamid_ex sc-status s-siteid s-reason s-queuename transport
The reason for each entry is s-reason, the third field from the end.
IIS FTP Service Logs#
If the server runs the IIS FTP service, IIS writes its logs to FTPSVC<n> folders under the same LogFiles directory, with the same u_ex*.log file names and a different layout (13 fields). The File Follower’s File-Filter matches file names only, so the iis follower in Gravwell Configuration below reads FTP logs into the iis tag too. To keep them out, move FTP logging to its own directory. Servers without the FTP service can skip this section.
Run these PowerShell commands as an administrator on the IIS server:
Back up the IIS configuration, using a new backup name:
Import-Module WebAdministration Backup-WebConfiguration -Name 'before-gravwell-ftp'
Check where FTP logs go today. The first command prints the server-wide FTP log directory,
C:\inetpub\logs\LogFileson a server where it was never changed. The second lists each FTP site and the directory it actually logs to; a site with its own directory shows a different path. The third lists FTP log folders in the directory theiisfollower reads; no output means there are none.(Get-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' ` -Filter 'system.applicationHost/sites/siteDefaults/ftpServer/logFile' -Name directory).Value Get-ChildItem IIS:\Sites | Where-Object { $_.Bindings.Collection.protocol -contains 'ftp' } | Select-Object Name, Id, @{n='LogDirectory';e={$_.ftpServer.logFile.directory}} Get-ChildItem C:\inetpub\logs\LogFiles -Directory -Filter FTPSVC* | Select-Object Name
Create the new directory and make it the server-wide FTP log directory:
New-Item -ItemType Directory C:\inetpub\logs\FTP -Force Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' ` -Filter 'system.applicationHost/sites/siteDefaults/ftpServer/logFile' -Name directory -Value 'C:\inetpub\logs\FTP'
Run the second and third commands from step 2 again. Every FTP site should show
C:\inetpub\logs\FTP. Each folder the third command lists holds FTP logs written before the move, and theiisfollower keeps reading them. Copy them elsewhere if you need them, then delete each one:Remove-Item -Recurse 'C:\inetpub\logs\LogFiles\FTPSVC<n>'
The change applies immediately, without restarting IIS, to running FTP sites and to FTP sites created later. A site with its own log directory keeps it.
Step 3 changes only
applicationHost.config, and it’s safe to run again.
To undo the move (safe to run more than once), clear the server-wide setting. New FTP rows go back to LogFiles at once, where the iis follower reads them:
Clear-WebConfiguration -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter 'system.applicationHost/sites/siteDefaults/ftpServer/logFile/@directory'
Or run:
Restore-WebConfiguration -Name 'before-gravwell-ftp'
This also rolls back every other IIS change made since the backup.
IIS Configuration Event Log#
Windows can record changes to the IIS configuration in the Microsoft-IIS-Configuration/Operational event log: the setting that changed, its old and new values, the account that made the change, and the ID of the process that saved it. Changes made with PowerShell or appcmd are recorded. Changes made by editing a configuration file directly are not. The log is off by default. Gravwell collects it with the Windows Event Ingester (see Gravwell Configuration).
Run these commands as an administrator on the IIS server:
Check whether the log is on.
enabled: falsemeans it is off.wevtutil gl Microsoft-IIS-Configuration/Operational
Turn it on. The command prints nothing, and it’s safe to run again.
wevtutil sl Microsoft-IIS-Configuration/Operational /e:true
Run step 1 again. It should show
enabled: true.
Changes made while the log is off are never recorded, and turning it on doesn’t add them later. Turn it on before making the logging changes in this guide, and they are recorded too. Undoing the FTP log move is the exception: it is recorded only as a save to the server configuration, without the setting or its old value.
The log keeps 1 MB and then overwrites its oldest events. Installing an IIS feature can fill most of it, so the copy in Gravwell is the lasting record.
To turn the log off again (safe to run more than once; events already in the log stay):
wevtutil sl Microsoft-IIS-Configuration/Operational /e:false
Gravwell Configuration#
Gravwell Storage Well Configuration#
Set up the well configuration in your Gravwell indexers.
Sample well config:
Create or edit: /opt/gravwell/etc/gravwell.conf.d/iis-well.conf
[Storage-Well "iis"]
Location=/opt/gravwell/storage/iis
Tags=iis*
Gravwell Ingester Configuration: Windows File Follower#
Install the Windows File Follower on the IIS server by following the instructions in Windows File Follower.
Sample IIS config:
Edit: %PROGRAMDATA%\gravwell\filefollow\file_follow.cfg
[Follower "iis"]
Base-Directory=`C:\inetpub\logs\LogFiles`
File-Filter=`u_ex*.log`
Tag-Name=iis
Recursive=true
Ignore-Line-Prefix="#"
[Follower "iis-httperr"]
Base-Directory=`C:\Windows\System32\LogFiles\HTTPERR`
File-Filter=`httperr*.log`
Tag-Name=iis-httperr
Ignore-Line-Prefix="#"
Recursive=truereads every site’s folder underLogFiles. On a server that runs the IIS FTP service, move the FTP logs first (see IIS FTP Service Logs), or theiisfollower reads them too.u_ex*.logalso matchesu_ex<YYMMDD>_x.log, the name IIS uses once a custom field such asX-Forwarded-Foris added. Don’t narrow it.httperr*.logfollows HTTP.sys from one numbered file to the next.The storage well above (
Tags=iis*) covers both tags, andiis-ftpbelow.No time zone setting is needed. IIS and HTTP.sys timestamps are UTC, which is what File Follower assumes for timestamps without a time zone.
Optionally, to collect FTP logs from the directory set in IIS FTP Service Logs, add the following:
[Follower "iis-ftp"]
Base-Directory=`C:\inetpub\logs\FTP`
File-Filter=`u_ex*.log`
Tag-Name=iis-ftp
Recursive=true
Ignore-Line-Prefix="#"
Note
Ignore-Line-Prefix="#" drops the header lines (#Software, #Version, #Date, #Fields), including those IIS writes mid-file after a restart or a logging change, and keeps every data line. Gravwell’s IIS queries don’t need the header lines. To keep them, remove that line from a follower: the #Fields lines are the only record in the data of which fields a server logs, which differs between servers (15, 17 or 21 or more fields, depending on their history) and changes whenever the logging settings do.
Note
Remember to restart the Gravwell File Follow service via standard Windows service management to apply the new config, for example Restart-Service 'GravwellFileFollower Service' in an administrator PowerShell.
After the restart, browse one of the sites, then search tag=iis count in Gravwell: it should return a count. tag=iis-httperr count may be empty on a quiet server, because HTTP.sys only writes an entry when it rejects something. IIS buffers FTP log writes, so FTP rows can reach the file several minutes after the session (row timestamps are the real event times); search tag=iis-ftp count after an FTP session and that delay.
Gravwell Ingester Configuration: Windows Event#
Install the Windows Event Ingester on the IIS server by following the instructions in Windows Event Ingester, then add a channel for the IIS configuration event log.
Sample Windows Event config:
Create or edit: %PROGRAMDATA%\gravwell\eventlog\config.cfg
[EventChannel "iis-config"]
Tag-Name=iis-config
Channel=Microsoft-IIS-Configuration/Operational
Max-Reachback=24h
The storage well above (
Tags=iis*) coversiis-config.Collect the System, Application and Security logs from the IIS server too, as described in Windows Event. They record application pool failures, ASP.NET events (on servers with ASP.NET installed) and sign-in failures on the web server.
Note
Remember to restart the Gravwell Windows Event service via standard Windows service management to apply the new config, for example Restart-Service 'GravwellEvents Service' in an administrator PowerShell.
After the restart, search tag=iis-config count in Gravwell. It stays empty until the next change to the IIS configuration.