I recently implemented Filebeat on my IIS server to parse and send logs to an ELK stack (Elasticsearch, Logstash, Kibana) for tracking threats and monitoring performance on my server. I have found that a few times a day one or more of our domains will get flooded with hundreds of requests. They are most often GET or POST requests for various .php files.
I know there are official ways to configure temporary blocks based on floods of requests using dynamic blocking in IIS. There are times that some of my sites might legitimately receive a flood of requests, so I have been hesitant to do that.
None of my sites run php so what I would love to have would be a tool or configuration that, at the server level, can watch request paths for php files and just blackhole all future requests from that IP for some period of time. I already have my server set up to automatically return a 404 for any php file, or any of the other frequently scanned paths like wordpress, before calling into the application code. Since I don't host php or wordpress, I know they aren't going to touch any of the files on my site but it still results in alerts and threats listed in my dashboard. The php scans are the most frequent ones that are clutter up my logs so even getting rid of those would be nice.
Ideally this theoretical tool/config/whatever would operate at the server level rather than requiring modification for each site but if it was a configuration change, rather than a code change, for each site, that would be ok.
As other said, we need to create or use a HTTP module for it, using native IIS HTTP module written in C#/C++. Take example with IISFrontGuard.Module – comprehensive WAF with rate limiting.
To configure IISFrontGuard.Module for rate limiting. *simpler way from my PoV
1. Requirements
- .NET Framework 4.8
- IIS 7.0 or later
- SQL Server 2012 or later *sadly it's the requirement as it save state/rule of it. can use sql server developer version
If you feel this is a barrier, then we could write our own module, then use SQLite
More Info https://github.com/kenllyacosta/IISFrontGuard *the direct project itself
2. Installation
Install the package via NuGet:
Install-Package IISFrontGuard.Module
The package automatically updates your Web.config with the required settings and opens a getting started guide.
3. Basic Rate Limiting Configuration
Rate limiting is configured via appSettings in Web.config:
<appSettings>
<!-- Maximum requests allowed per IP per window -->
<add key="RateLimitMaxRequestsPerMinute" value="150" />
<!-- Time window in seconds -->
<add key="RateLimitWindowSeconds" value="60" />
</appSettings>
This applies globally to all paths for your application.
4. Per-Path Rate Limiting (Using WAF Rules)
To apply different rate limits per path, you use the WAF rule engine stored in the SQL database.
Step 1: Set Up the Database
Execute the included SQL script to create the required tables:
Content\Scripts\init.sql
This creates the WafRules table where you define your rules.
Step 2: Create a WAF Rule for a Specific Path
Insert a rule that targets a specific path and applies rate limiting:
INSERT INTO WafRules (Name, Priority, IsEnabled, Action, Conditions)
VALUES (
'Rate Limit /login',
100, -- Priority (lower = higher priority)
1, -- IsEnabled (1 = enabled)
'Block', -- Action when rate limit is exceeded
'[{"Field":"Path","Operator":"Contains","Value":"/login"}]'
);
Step 3: Configure Per-Rule Rate Limits
The module's rate limit settings (RateLimitMaxRequestsPerMinute and RateLimitWindowSeconds) are global. For per-path limits, you need to:
- Set the global values to your default limit.
- Use database-driven rules with different actions.
- Or adjust the global values to your strictest limit and create allow rules for less-restricted paths.
Note: The current documentation does not show per-rule rate limit overrides. For advanced per-path limits, you may need to extend the module or use the global settings as the base and implement additional logic in your application.
5. Verifying the Module is Registered
Your Web.config should include this module registration:
<system.webServer>
<modules>
<add name="FrontGuardModule"
type="IISFrontGuard.Module.FrontGuardModule, IISFrontGuard.Module"
preCondition="managedHandler,runtimeVersionv4.0" />
</modules>
</system.webServer>
6. Additional Features
- GeoIP Filtering: Block or allow traffic based on country.
- Security Event Logging: Comprehensive logging to SQL database.
- Webhook Notifications: Real-time security event notifications.
Summary
| Configuration |
Method |
| Global rate limit |
appSettings in Web.config |
| Per-path rate limit |
WAF rules in SQL database with Path condition |
| Block action |
Set Action = 'Block' in the WAF rule |
For path-specific limits, use the WAF rule engine with conditions on the Path field. The global RateLimitMaxRequestsPerMinute and RateLimitWindowSeconds apply to all paths unless you implement additional logic.
Other than that, You can create a managed HTTP module by implementing the IHttpModule interface. This module hooks into the ASP.NET request pipeline and runs on every request.
A C# module runs inside the ASP.NET worker process and can inspect the path, track request counts per IP, and return a 403 when a threshold is exceeded.
Using C# Module Code
using System;
using System.Collections.Generic;
using System.Web;
using System.Configuration;
/// <summary>
/// IIS HTTP Module that dynamically blocks IPs based on request rate to specific paths.
/// </summary>
public class PathRateLimiterModule : IHttpModule
{
// In-memory tracker: IP -> (count, windowStart)
private static readonly Dictionary<string, Tracker> _tracker = new Dictionary<string, Tracker>();
private static readonly object _lock = new object();
// Configuration (read from web.config)
private static readonly int MAX_REQUESTS = int.TryParse(
ConfigurationManager.AppSettings["RateLimitMaxRequests"], out int max) ? max : 10;
private static readonly int WINDOW_SECONDS = int.TryParse(
ConfigurationManager.AppSettings["RateLimitWindowSeconds"], out int window) ? window : 60;
private static readonly string TARGET_PATH = ConfigurationManager.AppSettings["RateLimitTargetPath"] ?? "/login";
public void Init(HttpApplication context)
{
context.BeginRequest += OnBeginRequest;
}
public void Dispose() { }
private void OnBeginRequest(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;
var request = context.Request;
// 1. Check if the request matches the target path
string rawUrl = request.RawUrl ?? string.Empty;
if (!rawUrl.Contains(TARGET_PATH, StringComparison.OrdinalIgnoreCase))
return;
// 2. Get client IP
string ip = request.UserHostAddress;
if (string.IsNullOrEmpty(ip))
return;
// 3. Check threshold
DateTime now = DateTime.UtcNow;
lock (_lock)
{
if (!_tracker.TryGetValue(ip, out Tracker tracker))
{
_tracker[ip] = new Tracker { Count = 1, WindowStart = now };
return;
}
// Reset window if expired
if ((now - tracker.WindowStart).TotalSeconds >= WINDOW_SECONDS)
{
tracker.Count = 1;
tracker.WindowStart = now;
return;
}
// Increment and check threshold
tracker.Count++;
if (tracker.Count > MAX_REQUESTS)
{
// Block: return 403 and end request
context.Response.StatusCode = 403;
context.Response.StatusDescription = "Forbidden";
context.Response.End();
}
// You can add more logic with SQLite if you want... whatever it is just as a rule for it
}
}
private class Tracker
{
public int Count { get; set; }
public DateTime WindowStart { get; set; }
}
}
Web.config Configuration
Add these settings to your web.config:
<configuration>
<appSettings>
<add key="RateLimitMaxRequests" value="10" />
<add key="RateLimitWindowSeconds" value="60" />
<add key="RateLimitTargetPath" value="/login" />
</appSettings>
<system.web>
<httpModules>
<add name="PathRateLimiterModule" type="PathRateLimiterModule" />
</httpModules>
</system.web>
</configuration>
For IIS 7+ in Integrated Mode, use:
<system.webServer>
<modules>
<add name="PathRateLimiterModule" type="PathRateLimiterModule" />
</modules>
</system.webServer>
Deployment Steps
- Compile the code into a
.dll (Class Library project, .NET Framework 4.5+).
- Place the
.dll in your application's bin folder.
- Register the module in
web.config as shown above.
- Restart the application pool.
Important Notes
- This module runs after IIS authentication but before your page handler executes.
- The example uses in-memory storage. For production with multiple worker processes, use a shared store like Redis or SQL Server.
- The module only applies to ASP.NET requests (
.aspx, .ashx, MVC routes, etc.). For non-ASP.NET content (static files), IIS must be in Integrated Mode and the module must be registered under <system.webServer/modules>.
- A production version should add periodic cleanup of stale entries to prevent memory growth.
Official Documentation
Alternative: Pre-built NuGet Packages - Other than FrontGuard
If you prefer not to write code from scratch and not FrontGuard, consider these existing packages *I never use these, but seems promising:
- IISModuleAPIRateLimiter – supports URL pattern matching and configurable rate limits
- RateLimiter.IISModule – rate limiting by IP, API key, or custom headers