This article provides a foundational overview of the critical infrastructure layers that process a user's request before it reaches an application's backend code. It explains the roles of CDNs, WAFs, Load Balancers, API Gateways, Internal Reverse Proxies, Rate Limiters, and Input Sanitization. The content uses analogies to simplify complex concepts, making it accessible for those new to system design, and emphasizes the importance of each component in building robust, scalable, and secure applications.
Read original on Dev.to #systemdesignBefore an application's business logic even begins to execute, a user's request navigates through a sophisticated gauntlet of infrastructure components. Understanding this journey is fundamental to designing scalable, performant, and secure distributed systems. This overview highlights the key layers involved in handling a web request from the client to the backend.
Architectural Consideration: Layered Security and Performance
Each layer in the request journey contributes to the overall security, performance, and reliability of an application. System designers must carefully consider the placement and configuration of these components to optimize for specific requirements. For instance, caching at the CDN level reduces load on subsequent layers, while a WAF provides an early line of defense against attacks, complementing internal input sanitization.
While both API Gateways and Internal Reverse Proxies perform routing, their roles are distinct. An API Gateway acts as the external facing entry point, handling client-facing concerns like API versioning, authentication, and request aggregation for microservices. An Internal Reverse Proxy typically operates behind the API Gateway, managing internal routing between different microservices or groups of services, often in a more granular fashion without the broader API management responsibilities.
# Conceptual example of a rate limiter using Redis
import redis
import time
def is_rate_limited(user_id, limit, window_seconds):
r = redis.Redis(host='localhost', port=6379, db=0)
key = f'rate_limit:{user_id}'
current_time = int(time.time())
# Remove timestamps older than the window
r.zremrangebyscore(key, 0, current_time - window_seconds)
# Add current request timestamp
r.zadd(key, {current_time: current_time})
# Set expiration for the key
r.expire(key, window_seconds + 5) # +5 for buffer
# Check current count
count = r.zcard(key)
return count > limit
# Example Usage
# user_id = 'test_user_1'
# if is_rate_limited(user_id, 5, 60): # 5 requests per minute
# print('Rate limited!')
# else:
# print('Request allowed.')