22.4. Reverse Proxies
Table of Contents
What Is a Reverse Proxy?
A reverse proxy is a server that sits in front of one or more backend applications and receives all incoming client requests first. It then decides where to send those requests and passes the responses back to the client.
From the browser’s point of view, there is only one server: the reverse proxy. Your FastAPI, Django, or Node app sits behind it and is invisible to the outside world.
A very common setup:
Client (Browser)
|
v
Reverse Proxy (e.g. Nginx)
|
v
Application Server (e.g. Uvicorn/Gunicorn)You will almost always use a reverse proxy in production.
Key idea: A reverse proxy receives requests on behalf of your backend, forwards them to the right internal service, and returns the response. It is the public entry point to your backend.
Reverse Proxy vs Forward Proxy
A forward proxy (often just called "proxy") sits in front of clients. A reverse proxy sits in front of servers.
| Feature | Forward Proxy | Reverse Proxy |
|---|---|---|
| Sits in front of | Clients | Servers / backend apps |
| Typical users | Company employees, home users | Websites, APIs, production systems |
| Main purpose | Hide client, filter outbound traffic | Protect and manage backend servers |
| Client configuration | Clients must be configured to use it | Clients do not know it exists |
| Examples | Corporate proxy, VPN proxy | Nginx, Traefik, HAProxy as entry to backend |
Example of forward proxy usage:
- A company installs an HTTP proxy and forces all employee browsers to go through it. The proxy can block some sites and cache downloads.
Example of reverse proxy usage:
- Your domain
api.example.compoints to an Nginx server. Nginx forwards/api/*to your FastAPI app onlocalhost:8000and serves static files itself.
Why Use a Reverse Proxy in Backend Development?
You could run your FastAPI app directly on port 80, but in production this is almost never recommended. A reverse proxy gives you many benefits.
Security Benefits
A reverse proxy acts as a shield for your application servers.
Some security-related advantages:
- Hide internal network
Clients only see the reverse proxy IP. Your actual app might run on127.0.0.1:8000or10.0.0.5:8000, which is not accessible from the internet. - Filter and block bad traffic
The reverse proxy can: - Block requests from malicious IPs.
- Reject very large request bodies.
- Limit allowed HTTP methods.
- Add security headers.
- Terminate HTTPS
The proxy can handle TLS certificates and HTTPS, then forward plain HTTP to your app in the internal network. This keeps crypto and certificate management in one place. - Rate limiting
Many reverse proxies can limit requests per IP or per path, which helps against brute force attacks and simple DoS attempts.
Performance and Scalability Benefits
A reverse proxy can optimize traffic and reduce load on your backend.
Common features:
- Static file serving
Nginx or similar is very fast at serving static content like CSS, JS, images. Let it handle/static/*and only send dynamic requests to your Python app. - Keep-alive and connection reuse
The reverse proxy can maintain persistent connections with clients and backend servers, reducing the overhead of creating new connections. - Caching responses
For some endpoints, the proxy can cache responses. For example, a public products list that rarely changes. - Load balancing
You can run multiple instances of your backend and have the reverse proxy distribute requests between them.
Operational Benefits
Managing production systems is easier with a reverse proxy.
- Single entry point
All services are reachable via one endpoint, for example: https://example.comfor the main site,/api/for the API,/admin/for admin UI,
all behind the same reverse proxy.- Zero-downtime deployments
You can start new backend instances and stop old ones while the reverse proxy continues to accept requests, if configured correctly. - Central logging
The reverse proxy can log all incoming requests. This can be useful for analytics or debugging. - Protocol translation
The proxy can accept HTTPS/HTTP2 from clients and speak plain HTTP/1.1 to backend servers.
In practice: In production, always place your Python application server (Uvicorn, Gunicorn) behind a reverse proxy such as Nginx or Traefik.
Typical Reverse Proxy Architecture
Let us see a usual small production setup for a FastAPI app:
Internet
|
v
[Firewall]
|
v
[Reverse Proxy: Nginx] <– listens on :80 and :443
|
v
[App Server: Uvicorn / Gunicorn] <– listens on 127.0.0.1:8000Flow of a request:
- Browser sends
GET https://api.example.com/items. - DNS points
api.example.comto the IP of the server running Nginx. - Nginx terminates TLS and sees the HTTP request.
- Nginx has a rule like “for
/api/send tohttp://127.0.0.1:8000”. - Uvicorn receives
GET /itemsand returns a JSON response. - Nginx forwards that response back to the client.
Example: Basic Nginx Reverse Proxy Configuration
Imagine you have a FastAPI app running with Uvicorn on localhost:8000. You want to expose it on https://api.example.com.
A minimal Nginx configuration snippet could look like:
server {
listen 80;
server_name api.example.com;
# Redirect HTTP to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name api.example.com;
# TLS configuration (simplified)
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
# Important headers for WebSockets and many app servers
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Preserve original client info
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}What this does:
- Accepts HTTP on port 80 and redirects to HTTPS.
- Listens on port 443 with TLS certificates.
- For any path
/, forwards the request tohttp://127.0.0.1:8000. - Sends headers like
X-Forwarded-Forso your app can log the real client IP, not just127.0.0.1.
Preserving Client Information
By default, if Nginx listens publicly and your app runs on localhost, your app sees every request as coming from 127.0.0.1. To fix that, you rely on forwarded headers.
Common headers to set:
| Header | Meaning |
|---|---|
X-Real-IP | Client IP address |
X-Forwarded-For | Chain of IPs the request passed through |
X-Forwarded-Proto | Original protocol, http or https |
Host | Original host the client requested |
In application code, many frameworks know how to interpret these headers if configured properly.
Path-Based and Host-Based Routing
A reverse proxy can decide where to send a request based on:
- Host name
Routeapi.example.comto one backend andadmin.example.comto another. - Path prefix
Route/api/to the API,/static/to static files.
Examples:
Host-Based Routing
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8000; # FastAPI
}
}
server {
listen 80;
server_name admin.example.com;
location / {
proxy_pass http://127.0.0.1:9000; # Admin app
}
}Path-Based Routing
server {
listen 80;
server_name example.com;
location /api/ {
proxy_pass http://127.0.0.1:8000/;
}
location /static/ {
root /var/www/example; # Serves /var/www/example/static/*
}
location / {
proxy_pass http://127.0.0.1:9000/; # Frontend app
}
}This way, multiple services can share a single domain.
Load Balancing with a Reverse Proxy
A reverse proxy can distribute traffic across multiple backend instances. This improves reliability and sometimes performance.
Suppose you run three instances of your FastAPI app:
127.0.0.1:8001127.0.0.1:8002127.0.0.1:8003
You can define an upstream in Nginx:
upstream fastapi_backend {
server 127.0.0.1:8001;
server 127.0.0.1:8002;
server 127.0.0.1:8003;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://fastapi_backend;
}
}Nginx will send requests to each backend using a load balancing algorithm (default is round robin).
Benefits:
- If one instance crashes, the others can still handle some traffic.
- You can scale horizontally, by adding more backend instances.
Reverse Proxy and HTTPS Termination
In production, your API should be served over HTTPS. Managing certificates in every service is painful. A reverse proxy simplifies this.
TLS termination flow:
- Client connects with HTTPS to the reverse proxy.
- Reverse proxy handles TLS handshake with the client.
- Decrypted HTTP request is forwarded to your app over the internal network, usually plain HTTP.
- Response returns the same path, and the proxy sends encrypted data back to the client.
This is called TLS termination or SSL termination.
Advantages:
- One place to manage certificates (renew with Let’s Encrypt).
- Backend services can be simpler and speak plain HTTP inside a secure network.
Health Checks and Failover
A reverse proxy can monitor backend health.
Concepts:
- Health checks
The proxy periodically calls a special endpoint, for example/healthor/ping. If the backend stops responding correctly, the proxy will mark it as unhealthy. - Failover
With multiple backends, if one is unhealthy, the proxy stops sending it traffic and uses the remaining ones.
In some reverse proxies, you can configure custom health-check endpoints and intervals. This is common in load balancers provided by cloud providers and in advanced reverse proxies.
Some systems combine:
- A reverse proxy at the edge, and
- Separate load balancers or service discovery inside a microservices environment.
Reverse Proxy vs Application Server
Your Python application server (for example Uvicorn, Gunicorn, Uvicorn workers behind Gunicorn) and your reverse proxy are different roles.
| Component | Role |
|---|---|
| Application server | Runs your Python app, executes code, handles business logic |
| Reverse proxy | Manages incoming HTTP(S) traffic, routing, TLS, caching, balancing |
Why not expose Uvicorn directly?
- Uvicorn is designed as an application server. It can serve traffic, but:
- It does not handle TLS certificates as flexibly as Nginx or Traefik.
- It is not as optimized for static file serving.
- It does not provide all the proxy-level features like rich load balancing algorithms and advanced request filtering.
This is why you typically use both:
Client -> Reverse Proxy (Nginx/Traefik) -> Application Server (Uvicorn/Gunicorn) -> AppSimple Reverse Proxy Example with Traefik (Conceptual)
Traefik is another popular reverse proxy, often used with Docker. A simple idea:
- You label your Docker containers with rules.
- Traefik automatically routes requests based on these rules.
Example docker-compose fragment:
services:
reverse-proxy:
image: traefik:v2.10
command:
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
fastapi:
image: my-fastapi-image
labels:
- "traefik.enable=true"
- "traefik.http.routers.fastapi.rule=Host(`api.example.com`)"
- "traefik.http.services.fastapi.loadbalancer.server.port=8000"Here, Traefik:
- Listens on port 80.
- Routes requests with
Host: api.example.comto thefastapiservice on port 8000.
Common Pitfalls When Using Reverse Proxies
Beginners often run into similar problems.
1. Wrong Host or Scheme in the Application
Symptoms:
- Your app generates links with
http://when you expecthttps://. - Redirects go to
http://localhost:8000instead of your public domain.
Cause:
- The application sees the traffic as
httpand host127.0.0.1, not your real domain. - Missing
X-Forwarded-*headers or framework is not configured to trust them.
Fix:
- Ensure the reverse proxy sets
X-Forwarded-ProtoandHost. - Configure your framework to use these headers. Many frameworks have a setting or middleware for this.
2. Large Request Bodies Rejected
Symptoms:
- File uploads fail with 413 errors (payload too large).
Cause:
- Reverse proxy may limit maximum body size.
Fix:
- For Nginx, configure
client_max_body_size 20M;or similar in the rightserverorlocationblock.
3. Missing WebSocket Support
Symptoms:
- WebSocket connections close immediately or never upgrade.
Cause:
- Reverse proxy not configured to forward upgrade headers.
Fix:
- For Nginx, set:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";in the WebSocket location block.
Summary
A reverse proxy is a central building block of production backend systems. It:
- Sits in front of your application servers and receives all incoming requests.
- Forwards requests to backends, and returns responses to clients.
- Improves security by hiding internal services and filtering traffic.
- Enhances performance by handling static files, connection reuse, and caching.
- Enables scaling through load balancing across multiple backend instances.
- Simplifies HTTPS management with TLS termination.
As you move from development to production, learning how to configure and reason about reverse proxies like Nginx or Traefik is essential for running reliable, secure backend applications.
Views: 7
KAHIBARO