Layered System in REST — Proxies, Gateways, Firewalls, and CDN Architecture
In this tutorial, you will learn about Layered System in REST. We cover key concepts, practical examples, and best practices to help you master this topic.
The layered system constraint in REST allows intermediate components like proxies, gateways, firewalls, and CDNs to be inserted between client and server without requiring changes to either, enabling scalability, security, and performance optimizations.
What You'll Learn
- What the layered system constraint enables
- How to configure your API for layered deployments
- Common proxy headers and their purposes
Why It Matters
A well-architected layered system lets you add caching, load balancing, authentication, and rate limiting without touching application code. Your API server doesn't know if it's talking directly to a client or through five intermediate layers, because each layer only knows about the next.
Real-World Use
DodaTech's API runs behind six layers: Cloudflare CDN (edge caching), NGINX reverse proxy (rate limiting and TLS termination), Kong API Gateway (authentication and routing), a caching layer (Redis), the application server (Flask), and the database. Each layer is independently deployable and scalable.
flowchart LR
C["Client"] --> CDN["Cloudflare CDN\nEdge Cache"]
CDN --> NGINX["NGINX\nTLS + Rate Limit"]
NGINX --> GW["Kong Gateway\nAuth + Routing"]
GW --> Redis["Redis Cache\nLayer"]
Redis --> App["Application\nServer"]
App --> DB["Database"]
style C fill:#dbeafe,stroke:#2563eb
style App fill:#fef3c7,stroke:#d97706
style DB fill:#bbf7d0,stroke:#16a34a
Proxy Headers
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/status')
def status():
# The application sees the last proxy in the chain
return jsonify({
"client_ip": request.remote_addr,
"forwarded_for": request.headers.get('X-Forwarded-For'),
"forwarded_proto": request.headers.get('X-Forwarded-Proto'),
"forwarded_host": request.headers.get('X-Forwarded-Host'),
"real_ip": request.headers.get('X-Real-IP'),
"cf_connecting_ip": request.headers.get('CF-Connecting-IP'),
"cf_country": request.headers.get('CF-IPCountry'),
"cf_ray": request.headers.get('CF-Ray')
})
Trusted Proxy Configuration
from flask import Flask
from werkzeug.middleware.proxy_fix import ProxyFix
app = Flask(__name__)
# Configure trusted proxies
# num_proxies: number of proxy layers before the app
app.wsgi_app = ProxyFix(
app.wsgi_app,
x_for=1, # Number of X-Forwarded-For proxies
x_proto=1, # Number of X-Forwarded-Proto proxies
x_host=1, # Number of X-Forwarded-Host proxies
x_prefix=1 # Number of X-Forwarded-Prefix proxies
)
@app.route('/api/orders')
def get_orders():
# Now request.remote_addr returns the real client IP
client_ip = request.remote_addr
return jsonify({"client_ip": client_ip, "orders": get_orders_for_ip(client_ip)})
CDN Configuration
# When using a CDN like Cloudflare, configure cache purging
import requests
class CDNManager:
def __init__(self, api_token, zone_id):
self.api_token = api_token
self.zone_id = zone_id
self.base_url = f"https://api.cloudflare.com/client/v4/zones/{zone_id}"
def purge_url(self, url):
response = requests.post(
f"{self.base_url}/purge_cache",
headers={
"Authorization": f"Bearer {self.api_token}",
"Content-Type": "application/json"
},
json={"files": [url]}
)
return response.status_code == 200
def purge_all(self):
response = requests.post(
f"{self.base_url}/purge_cache",
headers={
"Authorization": f"Bearer {self.api_token}",
"Content-Type": "application/json"
},
json={"purge_everything": True}
)
return response.status_code == 200
cdn = CDNManager("your-api-token", "your-zone-id")
# Purge CDN cache when data changes
@app.route('/api/products/<int:product_id>', methods=['PUT'])
def update_product(product_id):
updated = update_product_in_db(product_id, request.json)
cdn.purge_url(f"https://api.dodatech.com/api/products/{product_id}")
return jsonify(updated)
Common Mistakes
1. Not Configuring ProxyFix
Behind a proxy, request.remote_addr returns the proxy IP, not the client IP. Without ProxyFix, all clients appear to come from the same IP.
2. Hardcoding Service Locations
In a layered system, service addresses change. Use service discovery (DNS, Consul, Kubernetes services) instead of hardcoded IPs.
3. Not Forwarding the Original Protocol
When NGINX terminates TLS, the app sees HTTP. Forward X-Forwarded-Proto so the app generates correct HTTPS URLs.
4. Allowing Direct Access to Internal Servers
Internal servers (Redis, database) must not be directly accessible from the internet. Layer security at the edge.
5. Ignoring Layer Latency
Each layer adds latency. Measure and optimize each layer. A six-layer system should still respond in under 200ms.
Practice Questions
- What is the purpose of the layered system constraint?
- What header carries the original client IP through proxies?
- How does ProxyFix middleware work?
- Why should you purge CDN cache after updates?
- How do you measure latency in a layered system?
Answers
- To allow intermediate components without affecting client or server code. 2. X-Forwarded-For header. 3. It reads proxy headers and updates request.remote_addr and other properties to reflect the original client. 4. To prevent users from receiving stale cached data after updates. 5. Use tracing headers (X-Request-Id, X-Trace-Id) and measure at each layer.
Challenge
Build a multi-layer API deployment with: a CDN caching layer (simulated), an NGINX reverse proxy, a Kong API Gateway for routing, and an application server. Configure proxy headers at each layer so the application correctly identifies the original client IP and protocol.
FAQ
Mini Project
Create a docker-compose setup with four layers: a Cloudflare-like caching layer (using Varnish), an NGINX reverse proxy, a Flask API server, and PostgreSQL. Configure all proxy headers to pass through correctly, and demonstrate that the application sees the real client IP and protocol.
What's Next
- Learn about code-on-demand as an optional REST constraint
- Explore resource naming conventions for consistent URI design
- Continue to resource relationships and sub-resources
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro