We've all been there - you discover a bug in your web application that causes certain requests to hang indefinitely. After fixing the bug, you notice one stubborn request still holding on to resources with Request ID 9f0002008001238e
. Recycling the entire application pool would disrupt other active sessions, which isn't ideal for production environments.
Standard IIS management tools don't provide a direct way to kill individual requests. While tools like IISPeek give visibility into active requests, they don't offer termination capabilities. The Windows Process Activation Service (WAS) manages requests at the worker process level, not per request.
Here are several approaches to handle this scenario:
1. Using AppCmd to Identify and Recycle Specific Worker Processes
While you can't kill a single request, you can target the specific worker process handling it:
# List all active worker processes %windir%\system32\inetsrv\appcmd list wp # Find the worker process with your request (look for the request ID in the command output) # Then recycle just that worker process %windir%\system32\inetsrv\appcmd recycle wp
2. Programmatic Approach with C#
For more control, you can create a custom module to monitor and terminate problematic requests:
using System; using System.Web; using Microsoft.Web.Administration; public class RequestTerminatorModule : IHttpModule { public void Init(HttpApplication context) { context.BeginRequest += (sender, e) => { var app = (HttpApplication)sender; if (app.Request.Headers["X-Request-ID"] == "9f0002008001238e") { app.CompleteRequest(); app.Context.Response.StatusCode = 410; // Gone app.Context.Response.End(); } }; } public void Dispose() { } }
3. Using Failed Request Tracing Rules
Configure tracing to automatically terminate requests that exceed a timeout:
<tracing> <traceFailedRequests> <add path="*"> <traceAreas> <add provider="ASP" verbosity="Verbose" /> </traceAreas> <failureDefinitions timeTaken="00:01:00" statusCodes="200-500" /> </add> </traceFailedRequests> </tracing>
Prevent future occurrences by adjusting executionTimeout in web.config:
<system.web> <httpRuntime executionTimeout="90" /> </system.web>
- Terminating requests abruptly may cause resource leaks or data corruption
- For stateful applications, implement proper cleanup in Application_Error
- Consider implementing health checks for long-running operations
- Log terminated requests for debugging purposes
For production environments, implement proactive monitoring:
# PowerShell script to monitor long-running requests Import-Module WebAdministration $longRequests = Get-ChildItem IIS:\AppPools | ForEach-Object { Get-WebRequest -AppPool $_.Name | Where-Object { $_.TimeElapsed -gt [TimeSpan]::FromMinutes(1) } } $longRequests | Export-Csv -Path "C:\logs\long_requests.csv" -NoTypeInformation
When dealing with long-running or hung requests in IIS, forcibly recycling the application pool is often the nuclear option - but what if you need surgical precision? This is particularly important in production environments where other critical processes are running.
Here's a typical situation you might face:
Request URL: https://example.com/buggy-page
Request ID: 9f0002008001238e
Client IP: 192.168.1.100
State: Executing (hung for 60+ minutes)
There are several ways to approach this in IIS:
- IISPeek (which you're already using)
- AppCmd.exe (built into IIS)
- PowerShell with IIS Administration Module
- Failed Request Tracing
The most precise method is using AppCmd with the request ID:
%windir%\system32\inetsrv\appcmd list requests /requestid:9f0002008001238e /xml |
%windir%\system32\inetsrv\appcmd delete requests /requestid:9f0002008001238e
This two-step command first identifies then kills the specific request. For multiple hung requests, you could:
# Get all requests running longer than 300 seconds
$longRequests = Get-IISAppPool |
Where-Object {$_.State -eq "Started"} |
Get-IISRequest |
Where-Object {$_.TimeElapsed -gt 300}
For more control, use the IISAdministration module:
Import-Module IISAdministration
$sm = Get-IISServerManager
$workerProcess = $sm.ApplicationPools["YourAppPool"].WorkerProcesses[0]
$requests = $workerProcess.GetRequests(0)
foreach ($request in $requests) {
if ($request.RequestId -eq "9f0002008001238e") {
$request.Abort()
Write-Host "Successfully terminated request $($request.RequestId)"
}
}
Before killing requests:
- Transactions might be left in an inconsistent state
- Database locks might not be released
- Partial file uploads might remain
For ASP.NET applications, consider implementing request timeouts in web.config:
<system.web>
<httpRuntime executionTimeout="110" />
</system.web>
To prevent future occurrences, set up:
<system.webServer>
<applicationPools>
<add name="MyAppPool"
autoStart="true"
queueLength="1000"
startMode="AlwaysRunning"
pingEnabled="true"
pingInterval="30"
pingResponseTime="90" />
</applicationPools>
</system.webServer>
Combine this with health monitoring to automatically detect and handle hung requests.