Modern web applications rarely expose their application servers directly to the public internet.
A production request commonly passes through several infrastructure layers before reaching the code responsible for generating a response:
Internet
│
▼
DNS
│
▼
CDN / Edge Network
│
▼
Reverse Proxy
│
▼
Load Balancing
│
├── Application Server 1
├── Application Server 2
└── Application Server 3 One of the most important components in this architecture is the reverse proxy.
A reverse proxy accepts requests on behalf of backend services and decides where those requests should go. In production environments, however, its responsibilities can extend far beyond forwarding traffic.
Reverse proxies can participate in TLS termination, application routing, load balancing, caching, compression, WebSocket proxying, rate limiting, authentication, security-header enforcement, request filtering, observability, and backend isolation.
Technologies such as Nginx, HAProxy, Traefik, and Caddy can implement different parts of this architecture.
Understanding reverse proxies is therefore essential for system administrators, network engineers, DevOps engineers, cloud architects, and developers working with APIs, containers, microservices, Kubernetes, Linux servers, or distributed applications.
This guide explains reverse proxy architecture from the network layer to production deployment, including Nginx configuration, TLS termination, load balancing, caching, headers, WebSockets, containers, microservices, security, high availability, observability, and troubleshooting.
For the infrastructure layer that frequently sits in front of a reverse proxy, read CDN Caching Explained. For the broader server architecture behind it, see Servers and Infrastructure Explained.
A reverse proxy is an intermediary server positioned between clients and backend application servers.
Without one:
Client
│
▼
Application Server With a reverse proxy:
Client
│
▼
Reverse Proxy
│
▼
Application Server The client communicates with the reverse proxy.
The proxy communicates with the backend.
For example, users might access:
https://example.com while the application itself listens internally at:
10.0.10.25:3000 The request path becomes:
Internet
│
▼
example.com:443
│
▼
Reverse Proxy
│
▼
10.0.10.25:3000 The internal application server does not need to be directly accessible from the public internet.
This creates an important architectural boundary between:
PUBLIC NETWORK and:
APPLICATION NETWORK Consider an application containing several services:
Frontend
API
Authentication
Admin Panel
Monitoring Dashboard Without a reverse proxy, administrators might expose multiple services directly:
example.com:3000
example.com:4000
example.com:5000
example.com:6000 This becomes difficult to manage and expands the externally exposed attack surface.
A reverse proxy creates a single controlled entry point:
Internet
│
▼
Reverse Proxy
│
┌─────────────┼─────────────┐
▼ ▼ ▼
Frontend API Admin
:3000 :4000 :5000 Users can instead access clean URLs:
example.com/
example.com/api/
admin.example.com/ while internal services remain on private addresses and ports.
This separation improves:
Reverse proxies and forward proxies are often confused.
A forward proxy represents clients.
Clients
│
▼
Forward Proxy
│
▼
Internet External servers communicate with the proxy rather than directly with each internal client.
Forward proxies can be used for:
A reverse proxy represents servers.
Internet
│
▼
Reverse Proxy
│
▼
Backend Servers The user communicates with the proxy without necessarily knowing which backend system processes the request.
A useful mental model is:
Forward Proxy
Client → Proxy → Internet
↑
Represents clients while:
Reverse Proxy
Internet → Proxy → Servers
↑
Represents servers Reverse proxying and load balancing overlap, but they describe different architectural responsibilities.
A basic reverse proxy can forward traffic to one backend:
Client
│
▼
Reverse Proxy
│
▼
Application A reverse proxy can also distribute traffic among several servers:
Reverse Proxy
│
┌───────────┼───────────┐
▼ ▼ ▼
Server A Server B Server C This introduces load balancing.
Therefore technologies such as Nginx can act as:
Reverse Proxy
+
Load Balancer Reverse proxying describes the intermediary role.
Load balancing describes the distribution of workloads.
For a deeper explanation of traffic distribution, see Cloud Load Balancing Explained.
Suppose a user requests:
https://example.com/products DNS resolves the hostname:
example.com
│
▼
203.0.113.20 That address belongs to the public-facing infrastructure.
The request arrives at the reverse proxy:
Browser
│
▼
Reverse Proxy The proxy examines information such as:
Hostname
URL Path
HTTP Method
Headers
Cookies
Routing Rules and selects a backend:
/products
│
▼
10.0.10.25:8080 The backend processes the request:
Reverse Proxy
│
▼
Backend
│
▼
Application Logic
│
▼
Database The response returns through the proxy:
Database
│
▼
Application
│
▼
Reverse Proxy
│
▼
Browser From the client’s perspective, communication happened with:
example.com not with:
10.0.10.25 Nginx is widely used as a web server, reverse proxy, HTTP cache, and load balancer.
A simple architecture might be:
Internet
│
▼
Nginx
│
▼
Node.js The Node.js application listens locally:
127.0.0.1:3000 while Nginx accepts external HTTP traffic.
A minimal configuration could look like:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
}
} The application remains on port 3000.
Users communicate through standard HTTP port 80.
In production, HTTPS normally replaces plain HTTP for the public connection.
The Nginx directive:
proxy_pass defines where matching requests should be forwarded.
Example:
location / {
proxy_pass http://127.0.0.1:3000;
} produces:
Browser
│
▼
Nginx :80/:443
│
▼
Application :3000 The backend could also exist on another server:
proxy_pass http://10.0.20.15:8080; or be represented by an upstream group:
upstream backend {
server 10.0.20.11:8080;
server 10.0.20.12:8080;
} followed by:
location / {
proxy_pass http://backend;
} Now the proxy can distribute requests across multiple application instances.
The official Nginx reverse proxy documentation explains the proxy directives and request-forwarding model in more detail.
When a reverse proxy connects to a backend, the backend sees the proxy as its immediate network peer.
Without additional information:
Client
│
▼
Proxy
│
▼
Backend the backend may identify the proxy address instead of the original client.
Important forwarding headers therefore commonly include:
Host
X-Real-IP
X-Forwarded-For
X-Forwarded-Proto A typical Nginx configuration is:
location / {
proxy_pass http://127.0.0.1:3000;
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;
} This allows the backend to understand the original request context.
Consider:
Client: 198.51.100.50
Proxy: 10.0.0.5
Backend: 10.0.0.10 At the TCP layer, the backend receives a connection from:
10.0.0.5 Without forwarding information, application logs may record every visitor as the proxy.
X-Forwarded-For can preserve the original client chain.
Conceptually:
X-Forwarded-For: 198.51.100.50 In architectures containing multiple proxies:
Client
│
▼
CDN
│
▼
Load Balancer
│
▼
Reverse Proxy the chain becomes more complicated.
Applications should trust forwarded headers only from known proxy infrastructure because arbitrary internet clients can otherwise attempt to spoof them.
Another important header is:
X-Forwarded-Proto Suppose:
Browser
│ HTTPS
▼
Reverse Proxy
│ HTTP
▼
Backend The backend’s direct connection is HTTP.
But the original client used HTTPS.
Without forwarded protocol information, the application may incorrectly believe the user accessed:
http://example.com This can cause:
The proxy can forward:
X-Forwarded-Proto: https so the application knows the original connection was encrypted.
A reverse proxy can route requests according to the hostname.
Suppose:
app.example.com
api.example.com
admin.example.com all point to one public reverse proxy.
The architecture becomes:
Reverse Proxy
│
┌──────────────┼──────────────┐
▼ ▼ ▼
app.example api.example admin.example
│ │ │
▼ ▼ ▼
Frontend API Admin Nginx can implement this using separate server blocks.
Example:
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:4000;
}
} This lets several applications share the same public server.
Routing can also depend on URL paths.
Example:
example.com/
│
└── Frontend
example.com/api/
│
└── API
example.com/media/
│
└── Media Service Nginx:
location / {
proxy_pass http://frontend;
}
location /api/ {
proxy_pass http://api;
}
location /media/ {
proxy_pass http://media;
} This is particularly useful for microservices.
One of the most important reverse proxy responsibilities is TLS termination.
Without termination at the proxy:
Client
│ HTTPS
▼
Application Server every application server needs to manage TLS.
With centralized TLS termination:
Client
│ HTTPS
▼
Reverse Proxy
│
│ HTTP or HTTPS
▼
Backend The reverse proxy handles:
TLS Handshake
Certificates
Cipher Negotiation
HTTPS while backend services can use internal HTTP or re-encrypted HTTPS depending on security requirements.
Imagine ten application servers.
Without centralized termination:
Server 1 → Certificate
Server 2 → Certificate
Server 3 → Certificate
...
Server 10 → Certificate With a reverse proxy:
Certificate
│
▼
Reverse Proxy
│
┌────────────┼────────────┐
▼ ▼ ▼
Backend Backend Backend Certificate management becomes significantly easier.
For public production services, certificate automation should generally be preferred over manual certificate replacement.
A common configuration redirects HTTP to HTTPS:
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
} Then HTTPS traffic is handled separately:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/fullchain.pem;
ssl_certificate_key /path/privkey.pem;
location / {
proxy_pass http://backend;
}
} Exact TLS configuration should follow current security guidance rather than copied legacy configurations.
TLS termination:
Client
│ HTTPS
▼
Proxy
│
▼
Decrypt
│
▼
Backend TLS passthrough:
Client
│ HTTPS
▼
Proxy
│ Encrypted
▼
Backend Termination gives the proxy access to HTTP information, enabling:
Path Routing
Header Manipulation
HTTP Caching
Application-Level Security Passthrough preserves end-to-end encryption to the backend but limits application-layer inspection at that proxy layer.
A third pattern is:
Client
│ HTTPS
▼
Reverse Proxy
│
│ HTTPS
▼
Backend The proxy terminates the client TLS connection and creates another TLS connection to the backend.
This can be useful when internal security requirements demand encryption between infrastructure components.
A reverse proxy can distribute traffic among backend servers.
Example:
upstream application {
server 10.0.10.11:8080;
server 10.0.10.12:8080;
server 10.0.10.13:8080;
}
server {
listen 80;
location / {
proxy_pass http://application;
}
} Architecture:
Nginx
│
┌────────────┼────────────┐
▼ ▼ ▼
App 1 App 2 App 3 This enables horizontal scaling.
A common distribution model is conceptually:
Request 1 → Server A
Request 2 → Server B
Request 3 → Server C
Request 4 → Server A This works well when servers have similar capacity and requests have roughly comparable cost.
Some workloads contain long-running requests.
For those systems, simply rotating requests may not provide balanced utilization.
Least-connections routing prefers the backend with fewer active connections.
Conceptually:
Server A → 100 connections
Server B → 20 connections
Server C → 60 connections New request:
→ Server B Nginx supports several upstream load-balancing methods, documented in its HTTP load balancing guide.
Not every server has equal capacity.
Suppose:
Server A = 16 CPU cores
Server B = 8 CPU cores Traffic can be weighted so that Server A receives a larger proportion.
Conceptually:
Server A → weight 2
Server B → weight 1 This is useful during:
Some applications store session state locally.
Suppose:
Login
│
▼
Server A creates local session data.
If the next request reaches:
Server B the session may disappear.
This creates pressure for sticky sessions:
User A → Server A
User A → Server A
User A → Server A But a more scalable architecture often externalizes state:
Servers
┌──────┼──────┐
▼ ▼ ▼
A B C
\ │ /
\ │ /
▼ ▼ ▼
Shared Session Store This allows any backend instance to process the request.
Stateless application design generally improves horizontal scalability.
Suppose:
Server A = Healthy
Server B = Failed
Server C = Healthy The proxy should avoid repeatedly sending traffic to Server B.
A resilient architecture requires backend failure detection and appropriate retry behavior.
Conceptually:
Reverse Proxy
│
├── A ✓
├── B ✗
└── C ✓ Traffic continues through healthy systems.
The exact health-check capabilities depend on the proxy product and edition, so production configurations should be verified against the relevant official documentation.
A reverse proxy can also cache responses.
Without caching:
Request
│
▼
Nginx
│
▼
Application
│
▼
Database With caching:
Request
│
▼
Nginx Cache
│
├── HIT ─────► Response
│
└── MISS
│
▼
Application This can reduce backend load substantially.
Nginx provides HTTP caching functionality documented in its content caching guide.
These layers are related but not identical.
A CDN cache is usually geographically distributed:
Global Users
│
▼
Global CDN A reverse proxy cache commonly exists close to the origin:
CDN
│
▼
Reverse Proxy Cache
│
▼
Application A production architecture may therefore contain both:
User
│
▼
CDN Cache
│
▼
Reverse Proxy Cache
│
▼
Application
│
▼
Database Each cache protects the layer behind it.
For detailed TTL, Cache-Control, cache-key, HIT/MISS, and invalidation concepts, see CDN Caching Explained.
A complete web application may contain:
Browser Cache
│
▼
CDN Cache
│
▼
Reverse Proxy Cache
│
▼
Application Cache
│
▼
Redis
│
▼
Database This architecture can be extremely efficient.
It can also become extremely confusing if nobody knows which layer owns freshness.
A production caching strategy should document:
What is cached?
Where?
For how long?
How is it invalidated?
Which headers control it? Without those answers, stale-data incidents become difficult to diagnose.
Reverse proxies can compress HTTP responses before sending them to clients.
For example:
Application
│
│ Large Response
▼
Nginx
│
▼
Compression
│
▼
Smaller Response Compression can reduce transferred bytes for suitable content types such as:
HTML
CSS
JavaScript
JSON
SVG But already-compressed formats such as many image, video, and archive formats usually provide little additional benefit.
Compression also consumes CPU, so configuration should reflect actual workloads.
Traditional HTTP often follows:
Request
│
▼
Response WebSockets create long-lived bidirectional connections:
Client
⇅
WebSocket
⇅
Server When a reverse proxy sits between them:
Client
⇅
Reverse Proxy
⇅
WebSocket Server the proxy must correctly handle the protocol upgrade.
A common Nginx pattern includes:
location /socket/ {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
} The official Nginx documentation covers WebSocket proxying.
This is relevant to:
A reverse proxy is not automatically a firewall, but it can become an important security-control point.
Architecture:
Internet
│
▼
Reverse Proxy
│
├── TLS
├── Request Validation
├── Rate Limiting
├── Header Policies
└── Routing
│
▼
Backend This provides a controlled boundary before requests reach application servers.
For broader network-security concepts, see Network Security Basics and Firewalls Explained.
One important design principle is:
Internet
│
▼
Reverse Proxy
│
▼
Private Backend Network rather than:
Internet
├── Reverse Proxy
├── Backend 1
├── Backend 2
└── Database Backend services should generally expose only the network interfaces and ports required for their role.
For example:
Internet
│
▼
Firewall
│
▼
Reverse Proxy :443
│
▼
Private Application Network The database should not become publicly accessible merely because the application uses it.
Hiding a backend address does not make the backend secure.
You still need:
Firewall Rules
Authentication
Authorization
Patching
TLS where required
Network Segmentation
Least Privilege
Monitoring A reverse proxy is one security layer—not a replacement for defense in depth.
A reverse proxy can restrict request frequency before traffic reaches the application.
Without rate limiting:
Client
│
├── Request
├── Request
├── Request
├── Request
└── Request × 10000
│
▼
Application With rate limiting:
Client
│
▼
Reverse Proxy
│
├── Allowed
└── Excessive → Reject / Delay This can protect:
Nginx provides request-rate controls through its limit_req module.
A simplified example:
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api_limit burst=20;
proxy_pass http://api_backend;
}
} Conceptually:
Client
│
▼
10 requests/sec policy
│
├── Within Limit → Backend
└── Excessive → Limited Actual values should be determined from application behavior, legitimate traffic patterns, and security requirements.
Arbitrarily low limits can block real users.
A reverse proxy can centralize HTTP security headers.
Examples include:
Strict-Transport-Security
X-Content-Type-Options
Content-Security-Policy
Referrer-Policy Conceptually:
Application
│
▼
Reverse Proxy
│
▼
Add Security Headers
│
▼
Browser This can create consistent policy across several backend services.
However, complex policies such as Content Security Policy should be designed according to the application’s actual resource requirements rather than copied blindly.
Reverse proxies can limit request sizes.
This helps prevent unexpectedly large uploads from reaching application infrastructure.
For example:
client_max_body_size 10m; can limit accepted request-body size for an applicable Nginx context.
But the correct limit depends on application requirements.
An API accepting profile images has different requirements from a video-upload service.
A reverse proxy can also participate in authentication architecture.
Conceptually:
User
│
▼
Reverse Proxy
│
▼
Authentication Check
│
├── Failed → 401 / Login
│
└── Valid
│
▼
Backend This is useful for:
However, backend applications containing sensitive authorization logic should not blindly assume that every request arriving from the proxy is automatically authorized.
Containers make reverse proxies especially useful.
Imagine:
Container A → Frontend :3000
Container B → API :4000
Container C → Auth :5000 These internal ports do not all need to be exposed publicly.
Instead:
Internet
│
▼
Reverse Proxy
│
┌────────────┼────────────┐
▼ ▼ ▼
Frontend API Auth
Container Container Container The proxy becomes the application’s public gateway.
A Docker deployment might use:
Public Network
│
▼
Reverse Proxy Container
│
▼
Private Docker Network
│
├── frontend
├── api
└── database Only the reverse proxy exposes:
80
443 while application services communicate through the internal container network.
This is generally cleaner than exposing every service directly to the host network.
Microservices increase routing complexity.
Suppose an application contains:
User Service
Order Service
Payment Service
Search Service
Notification Service A proxy or gateway can route:
/api/users/* → User Service
/api/orders/* → Order Service
/api/payments/* → Payment Service
/api/search/* → Search Service Architecture:
Client
│
▼
Proxy/Gateway
│
┌───────────────┼────────────────┐
▼ ▼ ▼
Users Orders Search
│ │ │
▼ ▼ ▼
Service Service Service As the architecture becomes more complex, however, the distinction between:
Reverse Proxy
API Gateway
Ingress Controller
Service Mesh becomes increasingly important.
They overlap, but they are not identical.
A reverse proxy focuses primarily on traffic forwarding and application delivery.
An API gateway may add API-specific capabilities such as:
Authentication
API Keys
Quotas
Request Transformation
API Analytics
Developer Policies
Version Routing Conceptually:
Reverse Proxy
│
└── Where should this HTTP request go? while an API gateway may ask:
Who is calling?
Are they authorized?
Which API plan applies?
Has the quota been exceeded?
Which version should process the request? Some products can perform both roles.
Kubernetes applications introduce another traffic-routing layer.
A simplified architecture is:
Internet
│
▼
Load Balancer
│
▼
Ingress / Gateway
│
▼
Kubernetes Service
│
▼
Pods Historically, Nginx has frequently been used as part of Kubernetes ingress architectures.
The underlying principle remains similar:
External Traffic
│
▼
Controlled Entry Point
│
▼
Routing
│
▼
Internal Services Kubernetes adds orchestration and dynamic service discovery around that model.
Static infrastructure might configure:
10.0.10.11
10.0.10.12
10.0.10.13 manually.
Container orchestration environments are more dynamic:
Container Starts
Container Stops
IP Changes
New Instance Created This creates a need for service discovery.
Instead of routing to a permanently fixed address:
10.0.10.11 the proxy architecture can integrate with a system that identifies currently available service instances.
This is one reason products designed for dynamic environments, such as Traefik, are frequently associated with containerized deployments.
A very common production design is:
Internet
│
▼
CDN
│
▼
Reverse Proxy
│
▼
Application The CDN handles:
Global Distribution
Edge Caching
Traffic Absorption
Potential Edge Security The reverse proxy handles:
Origin Routing
TLS architecture
Backend Selection
Local Caching
Application Policies The application handles:
Business Logic
Data Processing
Database Access This separation creates clearer infrastructure responsibilities.
A common example is:
Visitor
│
▼
Cloudflare
│
▼
Nginx
│
▼
WordPress / Node.js / Application
│
▼
Database The request crosses multiple proxy layers.
That means client-IP handling must be configured carefully.
Otherwise, Nginx may record Cloudflare’s proxy address instead of the actual visitor.
Cloudflare documents the visitor-IP headers used for proxied traffic in its HTTP request headers documentation.
Only trusted proxy networks should be allowed to influence the reconstructed client address.
A more advanced architecture is:
User
│
▼
CDN Cache
│
├── HIT → Response
│
└── MISS
│
▼
Reverse Proxy Cache
│
├── HIT → Response
│
└── MISS
│
▼
Application This provides multiple levels of origin protection.
But it also creates multiple TTLs:
Browser TTL
CDN TTL
Reverse Proxy TTL
Application Cache TTL Poor coordination can produce confusing stale-content behavior.
For the cache-control layer, refer to CDN Caching Explained.
A single reverse proxy can become a single point of failure.
Architecture:
Internet
│
▼
Reverse Proxy
│
X
│
Application If the proxy fails, the backend may remain healthy but unreachable.
Production systems may therefore deploy redundant proxy instances:
Traffic Entry
│
┌─────────┴─────────┐
▼ ▼
Reverse Proxy A Reverse Proxy B
│ │
└─────────┬─────────┘
▼
Backend Pool The public entry layer might be implemented using:
The exact solution depends on environment and failure requirements.
High availability requires more than adding a second server.
Ask:
Are both proxies in the same VM host?
Same rack?
Same availability zone?
Same network?
Same cloud region? Two proxy processes running on the same physical failure domain do not provide protection against that domain failing.
True availability architecture identifies independent failure boundaries.
This principle is explored more broadly in Servers and Infrastructure Explained.
A reverse proxy is an excellent observation point because almost every application request passes through it.
Logs can record:
Client IP
Timestamp
HTTP Method
URI
Status Code
Response Size
User Agent
Referer
Request Duration
Upstream Address
Upstream Response Time Conceptually:
Internet
│
▼
Reverse Proxy
│
├── Access Log
└── Error Log
│
▼
Backend These logs can help distinguish application problems from network or proxy problems.
Production monitoring should consider metrics such as:
Requests per Second
Active Connections
HTTP 2xx
HTTP 3xx
HTTP 4xx
HTTP 5xx
Request Latency
Upstream Latency
Connection Errors
Backend Availability
Cache HIT Ratio
TLS Errors Monitoring only CPU usage is not sufficient.
A proxy with low CPU can still be experiencing:
Backend Timeouts
Connection Exhaustion
DNS Failures
TLS Errors Complex architectures may include:
CDN
↓
Reverse Proxy
↓
API Gateway
↓
Service A
↓
Service B
↓
Database Troubleshooting becomes much easier when a request identifier travels through the entire chain.
Example:
X-Request-ID: 8ab71c Logs can then show:
CDN → 8ab71c
Reverse Proxy → 8ab71c
API → 8ab71c
Service → 8ab71c This allows engineers to reconstruct the path of one failing request across distributed infrastructure.
One of the most common reverse-proxy errors is:
502 Bad Gateway Conceptually:
Client
│
▼
Reverse Proxy ✓
│
▼
Backend ✗ The proxy is reachable, but something went wrong while communicating with the upstream service.
Possible causes include:
Application stopped
Wrong backend port
Connection refused
Backend crash
Protocol mismatch
DNS resolution failure
TLS problem to upstream
Malformed upstream response A 502 is therefore often an upstream communication problem, not proof that the reverse proxy itself is down.
Start by testing the backend directly from the proxy host.
Example:
curl http://127.0.0.1:3000 or:
curl http://10.0.10.25:8080 If this fails:
Reverse Proxy
│
X
Backend investigate the application or network path.
Check listening ports:
ss -lntp Check Nginx configuration:
nginx -t Inspect service state:
systemctl status nginx and application service status as appropriate.
Review logs:
journalctl -u nginx plus the configured Nginx error log and application logs.
For broader Linux administration techniques, see Linux Server Administration.
A:
503 Service Unavailable often indicates that a service cannot currently process the request.
Possible scenarios include:
No healthy backends
Application overloaded
Maintenance mode
Connection limits reached
Rate/capacity constraints Architecture:
Proxy
│
├── Backend A ✗
├── Backend B ✗
└── Backend C ✗ The proxy may have nowhere healthy to send the request.
A:
504 Gateway Timeout commonly indicates that the proxy waited too long for an upstream response.
Architecture:
Client
│
▼
Reverse Proxy
│
▼
Backend
│
...
│
...
│
TIMEOUT Potential causes include:
Simply increasing the proxy timeout can hide the symptom without solving the underlying performance problem.
Break the request path into layers:
Client
↓
Proxy
↓
Application
↓
Database
↓
External API Measure each one.
For example:
Proxy → Application = 5 ms
Application → Database = 8 seconds The reverse proxy is not the actual bottleneck.
Observability must follow the complete request path.
Basic headers:
curl -I https://example.com Verbose request:
curl -v https://example.com Measure timing:
curl -o /dev/null -s \
-w "connect=%{time_connect}\nstarttransfer=%{time_starttransfer}\ntotal=%{time_total}\n" \
https://example.com This can help separate:
Connection Time
Time to First Byte
Total Time For additional command-line diagnostics, see the Linux Network Tools Guide.
If the hostname is incorrect, the request may never reach the expected proxy.
Use:
dig example.com or:
dig +short example.com For DNS fundamentals, see DNS Explained.
Test whether a backend port is reachable:
nc -vz 10.0.10.25 8080 or where appropriate:
curl -v http://10.0.10.25:8080/ This helps distinguish:
Application Problem from:
Network Problem For multi-interface servers:
ip route may reveal incorrect routing.
More complex servers can require policy-based routing.
For those environments, see Linux Policy Routing with ip rule and Multiple Routing Tables.
A service may be running correctly but blocked by a firewall.
The path may look like:
Reverse Proxy
│
▼
Firewall
X
│
Backend Do not disable security controls blindly.
Instead verify that only the required source, destination, protocol, and port are permitted.
For network-security fundamentals, see Firewalls Explained.
A reverse proxy adds another network hop:
Client
↓
Proxy
↓
Backend but it can still improve overall performance through:
Caching
Compression
Connection Reuse
Load Distribution
TLS Offload
Static Content Delivery The relevant metric is not:
Did we add another server?
It is:
Did the complete request path become faster, more scalable, or more resilient?
Repeatedly creating backend connections can introduce overhead.
Reverse proxies can maintain and reuse upstream connections where appropriate.
Conceptually:
Request 1 ─┐
Request 2 ─┼──► Existing Backend Connection
Request 3 ─┘ rather than:
Request 1 → New Connection
Request 2 → New Connection
Request 3 → New Connection This becomes particularly useful at high request volumes.
A reverse proxy/web server can sometimes serve static assets directly:
Request
│
▼
Nginx
│
├── /assets/* → Static File
│
└── /api/* → Application This prevents application runtimes from handling requests that require no business logic.
Modern CDN architectures often push static delivery even further toward the edge.
The relationship can be represented as:
User
│
▼
Edge
│
▼
CDN
│
▼
Reverse Proxy
│
▼
Application Each layer solves a different problem.
Edge computing can execute selected logic close to users.
CDN caching prevents unnecessary origin requests.
Reverse proxying controls access to application infrastructure.
Load balancing distributes requests among backends.
Applications execute business logic.
For the distributed execution layer, see Edge Computing Explained.
In cloud environments, a reverse proxy may exist behind managed infrastructure.
Example:
Internet
│
▼
Cloud CDN
│
▼
Managed Load Balancer
│
▼
Nginx
│
▼
Application But not every architecture requires all layers.
For example:
Cloud Load Balancer
│
▼
Application may already provide enough functionality.
Adding Nginx only because “production architectures use Nginx” creates unnecessary complexity.
Every layer should have a defined responsibility.
A poor design can become:
CDN
↓
Load Balancer
↓
Reverse Proxy
↓
Another Reverse Proxy
↓
API Gateway
↓
Service Proxy
↓
Application Every layer introduces:
Configuration
Latency
Failure Modes
Logs
Cost
Operational Complexity Sometimes this complexity is justified.
Often it is not.
A strong architecture asks:
What unique responsibility does this layer provide?
If there is no clear answer, reconsider whether the layer is necessary.
A mature deployment might look like:
Internet
│
▼
DNS
│
▼
CDN / Edge
│
▼
Cloud Firewall
│
▼
Load Balancer
│
┌────────────┴────────────┐
▼ ▼
Reverse Proxy A Reverse Proxy B
│ │
└────────────┬────────────┘
▼
Application Tier
┌────────────┼────────────┐
▼ ▼ ▼
App A App B App C
│ │ │
└────────────┼────────────┘
▼
Data Layer Caching may exist at several points:
Browser
↓
CDN
↓
Reverse Proxy
↓
Application Cache
↓
Database Observability surrounds the entire path:
Logs
Metrics
Traces
Alerts Security is layered:
Edge Security
+
Firewall
+
TLS
+
Proxy Policy
+
Application Authentication
+
Authorization
+
Network Segmentation
+
IAM No individual component provides all of these protections.
A simplified production-oriented configuration might resemble:
upstream app_backend {
server 10.0.10.11:3000;
server 10.0.10.12:3000;
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/example/fullchain.pem;
ssl_certificate_key /etc/ssl/example/privkey.pem;
client_max_body_size 10m;
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
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;
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
}
} This example demonstrates several important ideas:
HTTP → HTTPS Redirect
TLS Termination
Backend Pool
Forwarded Headers
Connection Timeout
Response Timeout It should be adapted to the actual application rather than copied blindly into production.
Before exposing a production reverse proxy, verify:
Host forwarded correctly?A strong reverse-proxy architecture follows several principles.
Expose the minimum infrastructure required to serve public traffic.
Forward hostname, protocol, and client information correctly.
Only known proxy infrastructure should be allowed to establish trusted client metadata.
But re-encrypt backend connections when the threat model requires it.
A reverse proxy often reveals backend performance problems before users can identify the actual cause.
A 504 error is often a symptom.
Find the slow component before simply extending timeout values.
Never allow personalized responses to enter shared caches accidentally.
Assume:
Backend will fail.
Proxy will fail.
Network will fail.
DNS can fail.
Dependencies will fail. Architecture should define what happens next.
Production proxy configuration should live in version control whenever possible.
This connects naturally with Infrastructure as Code Explained.
A reverse proxy is an intermediary server that accepts client requests on behalf of backend services and forwards those requests according to routing and infrastructure policies.
Nginx can accept HTTP or HTTPS traffic and forward it to application servers using directives such as proxy_pass. It can also perform routing, load balancing, caching, TLS termination, and other HTTP infrastructure functions.
A forward proxy represents clients when accessing external resources. A reverse proxy represents backend servers when accepting requests from clients.
It can be both. Nginx can serve static web content directly while also forwarding requests to backend applications.
No. Reverse proxying describes an intermediary request-forwarding architecture. Load balancing describes distributing traffic among multiple destinations. A reverse proxy can also function as a load balancer.
Nginx can provide TLS termination, public-port handling, routing, caching, compression, logging, static-file delivery, and other infrastructure functionality while Node.js focuses on application logic.
Yes, as one layer of a broader security architecture. It can hide backend services, terminate TLS, limit requests, enforce selected HTTP policies, and reduce direct backend exposure. It does not replace firewalls, authentication, authorization, patching, or secure application design.
Yes. Products such as Nginx can cache eligible upstream responses, reducing application and database workload.
Yes. Nginx can distribute HTTP traffic across multiple upstream servers using supported load-balancing methods.
Common causes include an unavailable backend, incorrect address or port, connection refusal, protocol mismatch, upstream TLS errors, application crashes, or invalid upstream responses.
A 504 commonly occurs when the proxy does not receive an upstream response within the configured time. The underlying cause may be a slow application, database, external API, or network path.
They often complement each other. A CDN can provide global caching and edge delivery while the reverse proxy controls origin routing and backend access.
No. Managed cloud load balancers, application platforms, API gateways, and other proxies may already provide the required functionality. Infrastructure should solve defined requirements rather than adding layers automatically.
A reverse proxy appears simple:
Client
│
▼
Proxy
│
▼
Server but in production it can become one of the most important control points in the application architecture.
It can transform a collection of directly exposed services:
Frontend :3000
API :4000
Admin :5000
Service :6000 into a controlled architecture:
Internet
│
▼
CDN / Edge
│
▼
HTTPS :443
│
▼
Reverse Proxy
│
┌──────────────┼──────────────┐
▼ ▼ ▼
Frontend API Admin
│ │ │
└──────────────┼──────────────┘
▼
Data Layer At this point the reverse proxy can become responsible for:
Routing
TLS
Load Balancing
Caching
Compression
WebSockets
Rate Limiting
Request Metadata
Security Policies
Logging
Backend Isolation The most important architectural lesson, however, is not that every application needs every feature.
It is that every infrastructure layer should have a clearly defined responsibility.
The CDN should not exist merely because CDNs are popular.
The reverse proxy should not exist merely because Nginx is popular.
The load balancer should not exist merely because production diagrams contain one.
Each component should solve a measurable problem.
For a complete learning path, start with CDN Caching Explained to understand the cache layer that can sit ahead of the proxy.
Then use this reverse-proxy guide to understand origin routing.
Continue with Cloud Load Balancing Explained for backend traffic distribution and Servers and Infrastructure Explained for the systems underneath the application tier.
For distributed execution closer to users, continue with Edge Computing Explained.
For cloud network design, read Cloud Network Architecture: AWS vs Azure vs Google Cloud.
And for reproducible production infrastructure, continue with Infrastructure as Code Explained.
The resulting CorgiNetwork infrastructure learning path becomes:
DNS → Edge Computing → CDN Caching → Reverse Proxy → Load Balancing → Application Servers → Data Layer → Cloud Networking → Infrastructure as Code.
AI Agents as Code: Managing AI Agent Configurations, Prompts and Tools with Git AI agents…
Data Center Power Distribution Architecture: UPS, PDU, Redundancy and Rack Power Explained Data center power…
DNSSEC Troubleshooting: Fix SERVFAIL & DNSKEY Errors DNSSEC Troubleshooting: How to Fix SERVFAIL, DS, DNSKEY…
Terraform State Management Explained: Remote State, Locking, Backends and Best Practices Terraform makes infrastructure reproducible…
RTO vs RPO Explained: Disaster Recovery Objectives, Examples and Planning A disaster recovery plan is…
Network Intrusion Detection Explained: NIDS, Sensors, Signatures and Traffic Analysis A firewall can decide whether…