As APIs become more distributed, protecting backend services from excessive traffic is increasingly important. A single application may run across multiple instances behind a load balancer, making traditional in-memory rate limiting ineffective because each instance maintains its own request counters.
To enforce consistent request limits across all application instances, organizations commonly use a distributed data store such as Redis. Combined with YARP (Yet Another Reverse Proxy), Redis enables centralized rate limiting that scales with modern ASP.NET Core applications while protecting backend services from abuse, accidental traffic spikes, and excessive resource consumption.
In this article, you'll learn how distributed rate limiting works, how Redis and YARP complement each other, and how to design a production-ready solution for high-traffic ASP.NET Core applications.
Why Distributed Rate Limiting?
Consider an application running on three servers.
Load Balancer
│
┌─────────┼─────────┐
│ │ │
Instance1 Instance2 Instance3
If each server keeps its own request counter, a client can exceed the intended limit simply by sending requests to different instances.
Distributed rate limiting solves this problem by storing counters in a shared data store.
Common Use Cases
Distributed rate limiting is useful for:
Centralized limits help ensure fairness regardless of which application instance processes the request.
High-Level Architecture
Client
│
YARP Reverse Proxy
│
Redis
│
ASP.NET Core APIs
YARP receives incoming requests, checks the distributed rate limit using Redis, and forwards allowed requests to backend services.
Why Redis?
Redis is well suited for distributed rate limiting because it provides:
In-memory performance
Atomic operations
Key expiration
Distributed access
High throughput
These capabilities simplify the implementation of shared request counters.
Understanding Rate-Limiting Algorithms
Several algorithms are commonly used.
| Algorithm | Characteristics |
|---|
| Fixed Window | Simple implementation with fixed time intervals |
| Sliding Window | Smoother request distribution |
| Token Bucket | Allows controlled bursts |
| Leaky Bucket | Produces a steady request flow |
The appropriate algorithm depends on your traffic patterns and business requirements.
Request Flow
Client Request
│
YARP
│
Redis Counter
│
Limit Reached?
┌────┴────┐
│ │
No Yes
│ │
Forward HTTP 429
Requests exceeding the configured limit receive an appropriate response instead of reaching backend services.
Connecting to Redis
A Redis connection is typically shared throughout the application.
builder.Services.AddSingleton<IConnectionMultiplexer>(
ConnectionMultiplexer.Connect(connectionString));
Store connection information securely rather than embedding credentials directly in source code.
Creating a Rate Limiting Service
Encapsulate rate-limiting logic behind an abstraction.
public interface IRateLimiterService
{
Task<bool> AllowRequestAsync(
string clientId,
CancellationToken cancellationToken = default);
}
Separating the implementation makes the service easier to test and evolve.
Integrating with YARP
YARP supports ASP.NET Core middleware, allowing rate limiting to occur before requests reach backend APIs.
A simplified request pipeline:
Request
│
Authentication
│
Rate Limiting
│
Routing
│
Backend API
Applying limits early reduces unnecessary processing.
Identifying Clients
Rate limiting requires a consistent identifier.
Common options include:
API key
Authenticated user identifier
Client application identifier
Tenant identifier
IP address (when appropriate)
Choose an identifier that aligns with your authentication and authorization model.
Handling Exceeded Limits
When a limit is exceeded, return an appropriate response.
HTTP 429
Too Many Requests
Providing consistent responses helps client applications implement retry behavior when appropriate.
Monitoring Rate Limiting
Useful operational metrics include:
Monitoring helps identify traffic spikes and potential abuse.
Multi-Tenant Rate Limiting
Many SaaS applications require tenant-specific limits.
Example:
| Tenant Tier | Example Policy |
|---|
| Free | Lower request allowance |
| Standard | Moderate request allowance |
| Enterprise | Higher request allowance |
Exact thresholds should reflect business requirements and service capacity.
Security Considerations
Distributed rate limiting complements, but does not replace, other security measures.
Continue enforcing:
Authentication
Authorization
Input validation
TLS encryption
Audit logging
Rate limiting protects service availability rather than controlling user permissions.
Comparison of Rate-Limiting Approaches
| Approach | Advantages | Limitations |
|---|
| In-Memory | Simple implementation | Not suitable for multiple application instances |
| Redis-Based | Shared limits across instances | Requires external infrastructure |
| Gateway-Based | Centralized enforcement | Gateway becomes a critical component |
| Cloud Provider Services | Managed infrastructure | Platform-specific capabilities |
Organizations often combine gateway-based enforcement with distributed storage for consistency.
Common Mistakes
| Mistake | Better Approach |
|---|
| Using in-memory counters in distributed deployments | Store counters in Redis |
| Applying limits after business logic | Evaluate limits before routing requests |
| Identifying clients only by IP address | Prefer authenticated identities where available |
| Ignoring Redis availability | Plan for failure scenarios and monitoring |
| Logging sensitive authentication data | Log operational metadata instead |
Troubleshooting
Limits Are Not Consistent
Verify:
All application instances should reference the same distributed counter.
High Redis Latency
Investigate:
Performance issues may originate from infrastructure rather than the application.
Unexpected HTTP 429 Responses
Check:
Ensure policies match the intended workload.
Best Practices
Apply rate limiting at the gateway whenever practical.
Use Redis for distributed request counters.
Select client identifiers carefully.
Monitor Redis and gateway health.
Keep rate-limit policies configurable.
Test under representative traffic conditions.
Review policies regularly as application usage evolves.
Conclusion
Distributed rate limiting is an important architectural pattern for protecting high-traffic ASP.NET Core applications. By combining Redis with YARP Reverse Proxy, organizations can enforce consistent request limits across multiple application instances while maintaining scalability and operational flexibility.
A well-designed solution includes centralized request counters, careful client identification, continuous monitoring, and configurable policies. Together, these practices help improve application resilience, support fair resource usage, and protect backend services from excessive traffic without introducing unnecessary complexity.
Frequently Asked Questions
Why isn't in-memory rate limiting suitable for distributed applications?
Each application instance maintains its own counters, so requests distributed across multiple instances can bypass intended limits. A shared data store such as Redis provides consistent counting across the deployment.
Why use YARP for rate limiting?
YARP acts as a reverse proxy, allowing requests to be evaluated before reaching backend services. This helps reduce unnecessary processing and centralizes traffic management.
Should rate limits be based on IP addresses?
IP-based limits may be appropriate in some scenarios, but authenticated user identifiers, API keys, or tenant identifiers often provide more reliable and fair enforcement, especially in enterprise applications.
Can Redis become a single point of failure?
It can if deployed without appropriate resilience measures. Production environments typically use high-availability configurations, monitoring, and recovery strategies to reduce the impact of infrastructure failures.