Learning nginx - NGINX as a Reverse Proxy
Episode 6 of 21

Learning nginx - NGINX as a Reverse Proxy

This episode explains the concept of forward proxy versus reverse proxy, the basic proxy_pass configuration, forwarding important headers to the backend, and managing proxy connection timeouts and buffering.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

This is one of NGINX's most widely used roles in the industry: the reverse proxy. Modern applications like Node.js, Go, Python, and Java are almost always hidden behind NGINX, whether for security, performance, or flexibility. This Episode 6 covers the concepts and configuration.

You'll understand the difference between a forward proxy and a reverse proxy, write a correct proxy_pass directive, forward important headers so applications know the client's real address, and configure timeouts and buffering to keep proxy connections stable in production.

The Concept: Forward Proxy vs Reverse Proxy

Forward Proxy Represents the Client

A forward proxy sits in front of the client. The browser uses the proxy to reach the internet; outside servers only see the proxy's address, not the client's. Examples include office proxies or proxy-based VPN services.

Reverse Proxy Represents the Server

A reverse proxy sits in front of the server. Clients access the public domain, NGINX forwards requests to the backend, and the application is never directly exposed to the internet. Its benefits:

  • Backend applications don't need public ports; only NGINX is exposed.
  • TLS, caching, rate limiting, and compression are handled in one place.
  • Backends can be replaced, scaled, or moved without changing the client URL.
The simplest reverse proxy
server {
    listen 80;
    server_name app.example.com;
 
    location / {
        proxy_pass http://localhost:3000;
    }
}

Every request to app.example.com is forwarded to the application running on port 3000.

Basic proxy_pass Configuration

Without and With a Path

The proxy_pass directive can rewrite the URI as it's forwarded. This subtle difference often trips people up:

proxy_pass with and without a path
location / {
    proxy_pass http://localhost:3000;
}
 
location /api/ {
    proxy_pass http://localhost:3000;
}
 
location /static/ {
    proxy_pass http://localhost:3000/assets/;
}

When proxy_pass has no path (like http://localhost:3000), the request URI is forwarded as-is. When it has a path (like /assets/), the part of the URI matching the location is replaced. This is a rule you should hold onto tightly.

Using Variables in proxy_pass

NGINX also accepts variables in proxy_pass, for example when the backend is chosen based on a header:

proxy_pass with variables
server {
    listen 80;
    server_name app.example.com;
 
    set $backend http://localhost:3000;
 
    location / {
        proxy_pass $backend;
    }
}

Remember: once proxy_pass uses a variable, NGINX no longer performs automatic URI rewriting. Always test this combination with nginx -t.

Forwarding Essential Request Headers to the Backend

Headers That Must Be Forwarded

Without header forwarding, the application won't know where requests come from:

Important headers for the backend
location / {
    proxy_pass http://localhost: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;
}
  • Host $host — the domain name the client requested.
  • X-Real-IP — the client's real IP.
  • X-Forwarded-For — the list of IPs behind the proxy.
  • X-Forwarded-Proto — the original scheme, http or https.

Many frameworks read these headers for logging, per-IP rate limiting, and building absolute URLs. Never trust them without verification, because applications that assume X-Forwarded-For can't be manipulated are vulnerable to spoofing.

Managing Proxy Timeouts and Buffering

Connection Timeouts

A slow backend can hang NGINX workers. Set time limits:

Proxy timeouts
location / {
    proxy_pass http://localhost:3000;
 
    proxy_connect_timeout 10s;
    proxy_read_timeout 60s;
    proxy_send_timeout 60s;
}
  • proxy_connect_timeout — the time limit for establishing a connection to the backend.
  • proxy_read_timeout — the time limit for waiting for a response from the backend.
  • proxy_send_timeout — the time limit for sending a request to the backend.

Buffering

NGINX holds the backend's response in a buffer before sending it to the client:

Buffering configuration
location / {
    proxy_pass http://localhost:3000;
    proxy_buffering on;
    proxy_buffer_size 8k;
    proxy_buffers 8 8k;
}

Buffering keeps a slow client from holding up the backend. For realtime applications like SSE or streaming, buffering is actually turned off — details in episode 13.

Conclusion

Episode 6 solidified NGINX's role as a reverse proxy: you understand the difference between forward and reverse proxies, write proxy_pass with and without a path, forward important headers, and configure timeouts and buffering.

Key takeaways:

  • A reverse proxy represents the server; a forward proxy represents the client.
  • proxy_pass without a path forwards the original URI; with a path it rewrites the URI.
  • You must forward Host, X-Real-IP, X-Forwarded-For, and X-Forwarded-Proto.
  • Don't trust X-Forwarded-For from outside without validation.
  • Proxy timeouts prevent workers from waiting on slow backends forever.
  • Buffering protects the backend from slow clients.

In the next episode we'll discuss load balancing algorithms and upstream management — defining a backend cluster with the upstream block, the round robin, least connections, IP hash, and generic hash algorithms, plus passive health checks and server states.

Learning nginx - NGINX as a Reverse Proxy | Learning nginx