Isaac.

Implementing Rate Limiting

Protect endpoints from abuse with rate limiting strategies.

By EMEPublished: February 20, 2025
rate limitingapi protectionmiddlewarethrottling

A Simple Analogy

Rate limiting is like a speed limit on highways. It prevents abuse by restricting how fast (many requests) can happen.


Why Rate Limiting?

  • Abuse prevention: Block malicious clients
  • Resource protection: Don't overload servers
  • Fair access: Prevent monopolization
  • Cost control: Limit API usage costs
  • DoS protection: Mitigate attacks

Per-IP Rate Limiting

public class IpRateLimitMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ConcurrentDictionary<string, RateLimit> _limits;
    
    public IpRateLimitMiddleware(RequestDelegate next)
    {
        _next = next;
        _limits = new ConcurrentDictionary<string, RateLimit>();
    }
    
    public async Task InvokeAsync(HttpContext context)
    {
        var ip = context.Connection.RemoteIpAddress.ToString();
        var limit = _limits.GetOrAdd(ip, _ => new RateLimit(100, TimeSpan.FromMinutes(1)));
        
        if (!limit.IsAllowed())
        {
            context.Response.StatusCode = 429;
            context.Response.Headers.Add("Retry-After", "60");
            await context.Response.WriteAsync("Rate limit exceeded");
            return;
        }
        
        await _next(context);
    }
    
    public class RateLimit
    {
        private int _remaining;
        private DateTime _resetTime;
        
        public RateLimit(int limit, TimeSpan window)
        {
            _remaining = limit;
            _resetTime = DateTime.UtcNow.Add(window);
        }
        
        public bool IsAllowed()
        {
            if (DateTime.UtcNow >= _resetTime)
            {
                _remaining = 100;
                _resetTime = DateTime.UtcNow.AddMinutes(1);
            }
            
            if (_remaining > 0)
            {
                _remaining--;
                return true;
            }
            
            return false;
        }
    }
}

Header-Based Response

context.Response.Headers.Add("X-RateLimit-Limit", "100");
context.Response.Headers.Add("X-RateLimit-Remaining", "45");
context.Response.Headers.Add("X-RateLimit-Reset", "1645000000");

// Or
context.Response.Headers.Add("Retry-After", "60");  // seconds to wait

Sliding Window

public class SlidingWindowCounter
{
    private readonly Queue<DateTime> _requests = new();
    private readonly int _limit;
    private readonly TimeSpan _window;
    
    public SlidingWindowCounter(int limit, TimeSpan window)
    {
        _limit = limit;
        _window = window;
    }
    
    public bool IsAllowed()
    {
        var now = DateTime.UtcNow;
        var cutoff = now.Subtract(_window);
        
        // Remove old requests
        while (_requests.Count > 0 && _requests.Peek() < cutoff)
        {
            _requests.Dequeue();
        }
        
        if (_requests.Count < _limit)
        {
            _requests.Enqueue(now);
            return true;
        }
        
        return false;
    }
}

Endpoint-Specific Limits

[RateLimit(limit: 100, window: 60)]  // 100 requests per 60 seconds
[HttpGet("products")]
public async Task<IActionResult> GetProducts()
{
    return Ok(await _service.GetProductsAsync());
}

[RateLimit(limit: 10, window: 60)]  // Stricter for writes
[HttpPost("products")]
public async Task<IActionResult> CreateProduct(CreateProductRequest req)
{
    var product = await _service.CreateAsync(req);
    return CreatedAtAction(nameof(GetProducts), new { id = product.Id });
}

Best Practices

  1. Be generous: Don't limit legitimate users
  2. Inform clients: Use headers clearly
  3. Transparent: Document limits in API docs
  4. Graceful degradation: Queue instead of reject
  5. Monitor: Track limit violations

Related Concepts

  • Quota management
  • Throttling
  • Traffic shaping
  • DDoS protection

Summary

Implement rate limiting to protect endpoints from abuse. Use per-IP tracking, sliding windows, and clear response headers.