GICT knowledge base
How Reverse Proxies Work and Why Nginx Is So Popular
Published
If you have spent any time around web servers, you have probably set up Nginx as a reverse proxy without fully understanding what that meant. You followed a tutorial, copy-pasted some proxy_pass lines, and it worked. Most developers stop there. But reverse proxies are doing a lot more than just forwarding requests, and understanding what they actually do makes you better at debugging the weird issues that show up in production.
In this article, you will learn what a reverse proxy actually is, how it differs from a regular proxy, what jobs reverse proxies handle in modern web infrastructure, why Nginx ended up dominating this space, and how to set up a basic reverse proxy configuration that you can adapt to real applications.
Prerequisites
Before you start, make sure you have:
- A Linux machine running Ubuntu 22.04, Debian 12, or a similar distribution.
- A user account with sudo access.
- Nginx installed and running.
- Some kind of backend application to proxy to, like Node.js, Python, or a PHP-FPM service.
- Basic familiarity with HyperText Transfer Protocol (HTTP).
What a Reverse Proxy Actually Does
A reverse proxy is a server that sits in front of one or more backend servers and forwards client requests to them. From the client’s perspective, the reverse proxy is the application. The client never knows there is anything behind it. Whatever responds to the request looks like a single server, even if there are ten servers handling the actual work.
This is the opposite of a forward proxy, which sits in front of clients instead of servers. A forward proxy is what your office uses to filter web traffic. A reverse proxy is what a website uses to handle incoming traffic. Same technology, different direction.
The simplest reverse proxy setup looks like this. A user requests https://example.com/api/users. The request hits Nginx. Nginx forwards the request to the actual application running on localhost:3000 or wherever the backend lives. The backend responds, Nginx passes the response back to the user, and the user has no idea the request went anywhere except to Nginx.
Why bother with the extra layer? Because that extra layer is where you put all the things that should not be in your application code.
What Reverse Proxies Actually Handle
Once you have a reverse proxy in front of your application, it becomes the natural place to handle a long list of jobs that would be painful to do anywhere else.
Transport Layer Security Termination
Almost every modern site runs over HyperText Transfer Protocol Secure (HTTPS), which means somewhere there is a Transport Layer Security (TLS) certificate doing encryption. You could put the certificate on every backend server and have each one handle TLS, but that gets messy fast. The reverse proxy handles it instead.
The proxy terminates TLS, meaning it decrypts incoming requests and re-encrypts outgoing responses. The backend application talks to the proxy over plain HTTP on the local network and never deals with certificates. One certificate, one place to renew it, one place to configure cipher suites. Much cleaner.
Load Balancing
When you have more than one backend server, something has to decide which one gets each request. The reverse proxy is the obvious place. Nginx supports several load balancing methods, from simple round-robin to weighted distribution to least-connections.
The application code stays the same whether there are one or fifty backend servers. The proxy handles the distribution, and adding or removing servers is a configuration change instead of a code change.
Caching
Some responses do not change often. Static files, API responses for public data, anything where the same request returns the same answer for minutes or hours. Caching those at the proxy level means the backend never sees most of the requests for that data.
A well-configured cache can reduce backend load by 80 percent or more on read-heavy applications. The backend still produces the responses, but only when the cache misses or expires.
Compression
Browsers support gzip and Brotli compression, which can shrink text-based responses by 60 to 80 percent. The reverse proxy can handle compression on the way out, so the backend just produces the raw response and the proxy compresses it before sending it to the client.
This keeps compression logic out of the application and centralizes it in one place where it can be tuned consistently across all responses.
Header Manipulation
Reverse proxies can add, remove, or modify HTTP headers on requests and responses. Setting security headers like Strict-Transport-Security, X-Content-Type-Options, and Content-Security-Policy happens at the proxy. Stripping headers that leak internal information happens at the proxy. Adding tracing headers for observability happens at the proxy.
You can do all of this in the application, but doing it at the proxy means it applies uniformly to every response without each application needing to remember.
Rate Limiting and Request Filtering
Blocking abusive clients, throttling expensive endpoints, rejecting malformed requests. The reverse proxy is the right place for all of it because it filters bad traffic before it ever reaches the backend.
A request that gets rejected at the proxy uses almost no resources. A request that reaches the application and gets rejected there has already cost you database queries, application processing, and memory. Stopping abuse at the edge is much cheaper.
Path Routing
A single domain can serve many different backend services, with the proxy deciding which one handles each path. Requests to /api/ go to the API server, requests to /admin/ go to the admin panel, requests to /static/ get served directly from disk, and requests to / go to the main application.
The frontend looks like one unified application even though it might be five separate services running on five different ports.
Why Nginx Specifically
There are other reverse proxies. HAProxy, Apache with mod_proxy, Caddy, Traefik, and Envoy all do similar work. So why did Nginx end up everywhere?
The Architecture Was Right at the Right Time
Apache, the dominant web server before Nginx, used a process-per-connection or thread-per-connection model. That worked fine in 2000 but started showing its limits as websites grew. Each connection cost real memory, and high-traffic sites needed enormous amounts of RAM to handle modest concurrency.
Nginx took a different approach. It uses a small number of worker processes, each handling thousands of connections through an event-driven, non-blocking model. The same hardware that ran Apache at 1000 concurrent connections could run Nginx at 50,000 without breaking a sweat.
This timing mattered. Nginx came onto the scene right as websites were getting big enough that Apache’s model started to hurt. The performance difference was not subtle. It was 10x or more on connection-heavy workloads.
The Configuration Made Sense
Nginx configuration is not perfect. The syntax has quirks, the documentation has gaps, and the semantics of certain directives can surprise you. But compared to Apache’s .htaccess files and verbose XML-influenced configs, Nginx felt clean.
A simple reverse proxy in Nginx is a few lines. The same setup in Apache requires more modules, more directives, and more attention to ordering. For administrators setting up dozens of sites, the cleaner config saved real time.
It Did the Right Things by Default
Nginx ships with sensible defaults. It serves static files efficiently. It handles compression. It deals with TLS. It supports HyperText Transfer Protocol version 2 (HTTP/2) and version 3 (HTTP/3). All of this works out of the box with minimal configuration.
For most use cases, you do not need to know much about Nginx internals to use it well. Drop in a config, point it at your backend, and it just works. That low barrier to entry helped adoption enormously.
The Ecosystem Caught Up
Once Nginx hit critical mass, everyone started writing tutorials and example configurations for it. Hosting platforms standardized on it. Container images came preconfigured with it. Stack Overflow filled up with answers. At a certain point, picking anything else meant forgoing all of that accumulated knowledge.
The ecosystem advantage is real and self-reinforcing. Nginx is popular partly because it is good and partly because it is popular.
A Basic Nginx Reverse Proxy Configuration
Setting up Nginx as a reverse proxy is straightforward once you have a backend running. The example below assumes you have an application running on localhost:3000.
Create a new site configuration.
sudo nano /etc/nginx/sites-available/example.com
Add a basic reverse proxy block.
nginx
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:3000;
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;
}
}
The proxy_set_header lines pass information about the original request to the backend, which the backend usually needs to log requests correctly and generate proper URLs. Without them, every request looks like it came from the proxy itself.
Enable the site.
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
Test the configuration.
sudo nginx -t
Reload Nginx.
sudo systemctl reload nginx
That is a working reverse proxy. Real configurations add TLS, caching, compression, and other features, but this is the foundation everything else builds on.
Adding TLS with Let’s Encrypt
A reverse proxy without HTTPS is not really useful in production. Let’s Encrypt makes adding it almost trivial.
Install Certbot and the Nginx plugin.
sudo apt install certbot python3-certbot-nginx
Run Certbot against your site.
sudo certbot --nginx -d example.com
Certbot reads your existing Nginx configuration, requests a certificate from Let’s Encrypt, and modifies the config to use the new certificate. It also sets up automatic redirects from HTTP to HTTPS.
Verify the renewal will work.
sudo certbot renew --dry-run
After this, your reverse proxy handles TLS termination automatically, and the certificate renews itself every 60 days.
Adding Basic Load Balancing
Load balancing across multiple backends takes a few extra lines. Define an upstream block listing the backends, then proxy to that upstream instead of a specific address.
Edit your site configuration.
sudo nano /etc/nginx/sites-available/example.com
Add an upstream block above the server block.
nginx
upstream backend {
server 10.0.0.1:3000;
server 10.0.0.2:3000;
server 10.0.0.3:3000;
}
server {
listen 443 ssl;
server_name example.com;
location / {
proxy_pass http://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;
}
}
Nginx distributes requests across the three backend servers using round-robin by default.
For a different distribution method, add it to the upstream block.
nginx
upstream backend {
least_conn;
server 10.0.0.1:3000;
server 10.0.0.2:3000;
server 10.0.0.3:3000;
}
The least_conn directive sends each new request to the backend with the fewest active connections, which works better than round-robin when request times vary a lot.
Reload Nginx.
sudo systemctl reload nginx
[!NOTE] The free version of Nginx does not include active health checks. If a backend goes down, Nginx will keep sending requests to it until they fail. There are workarounds, but for serious production setups with many backends, the commercial Nginx Plus or alternatives like HAProxy might be worth considering.
Adding Caching
Caching at the proxy can dramatically reduce backend load. The setup involves defining a cache zone and then using it in your location blocks.
Edit the main Nginx configuration.
sudo nano /etc/nginx/nginx.conf
Add a proxy_cache_path directive in the http block.
nginx
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:10m max_size=1g inactive=60m;
This creates a 1 GB cache stored on disk, keeping cached items for up to 60 minutes of inactivity.
Use the cache in your site configuration.
nginx
location /api/public/ {
proxy_cache app_cache;
proxy_cache_valid 200 5m;
proxy_cache_use_stale error timeout updating;
proxy_cache_lock on;
proxy_pass http://backend;
}
The proxy_cache_valid line caches successful responses for 5 minutes. The proxy_cache_use_stale directive lets Nginx serve stale cached content if the backend has trouble, which keeps the site usable during backend issues.
Reload Nginx.
sudo systemctl reload nginx
[!WARNING] Be careful what you cache. Caching responses that depend on user authentication or session state can leak data between users. Only cache responses that are genuinely public or include cache key components that vary by user.
When Nginx Is Not the Right Choice
Nginx is excellent, but it is not always the best tool. A few cases where alternatives make sense:
- Service mesh and microservices: Envoy and Linkerd are designed for service-to-service communication inside a cluster, with features like dynamic configuration, observability, and circuit breaking that Nginx does not handle natively.
- Heavy Layer 4 load balancing: HAProxy has historically been better at raw Transmission Control Protocol (TCP) load balancing and offers more sophisticated load balancing algorithms in its free version.
- Automatic certificate management out of the box: Caddy handles Let’s Encrypt automatically with zero configuration. For simple setups, that convenience can outweigh other factors.
- Dynamic configuration in container environments: Traefik integrates directly with Docker, Kubernetes, and other orchestrators, picking up configuration from labels and metadata. Nginx can do this with extra tooling, but Traefik makes it the default.
For most traditional web hosting and reverse proxy needs, Nginx is still the right answer. For modern containerized infrastructure, the choice gets more interesting.
Conclusion
You now know what a reverse proxy actually is, the jobs it handles in real infrastructure, why Nginx came to dominate this space, and how to set up basic reverse proxy configurations including TLS termination, load balancing, and caching. You also know which alternatives exist and when they might fit better.
Reverse proxies are one of the highest-leverage pieces of infrastructure you can deploy. A few hundred lines of Nginx configuration replace what would otherwise be tangled application code spread across every service. The investment in understanding what your proxy is doing pays off the first time something breaks at 2 AM and you actually know where to look.
Related guides
Load Balancing Algorithms Explained: When to Use Which Algorithm Simplified
A detailed breakdown of load balancing algorithms, health checks, layer 4 vs layer 7, and how traffic actually gets distributed across servers.
Server ManagementHow to Fix “Too Many Open Files” Error on Linux Servers
Learn how to fix the "Too Many Open Files" error on Linux servers by understanding file descriptors, adjusting limits, and diagnosing leaks effectively.
Server ManagementHow to Increase PHP Upload Max Filesize on Apache and Nginx
Learn how to increase PHP upload max filesize on Apache and Nginx, including configuration settings, common issues, and approaches for handling large file uploads.
Server ManagementUnderstanding Server Monitoring and Why It Matters
Explore the importance of server monitoring, its key metrics, and why proactive monitoring is essential for maintaining server health and performance.
