Nginx: The Production Standard
Objectives
By the end of this lesson, you should be able to:
- Explain why Nginx, not a hand-rolled Node.js proxy, is what most real deployments actually use
- Write a basic Nginx configuration that reverse-proxies to a Node.js application
- Explain what a load-balanced Nginx upstream block does
💡 Why this matters: Lesson 1’s Node.js reverse proxy is genuinely working code, and a great way to understand the mechanism, it’s also not what a real production deployment typically runs, this lesson covers what almost every real Node.js deployment actually puts in front of it instead, and why.
⚠️ A note on verification: Nginx isn’t installable in this course’s own sandboxed tooling (no root access to install system packages). The configuration below reflects Nginx’s stable, current, documented syntax and behavior. Install Nginx on your own machine, or use a managed platform that provisions it for you (Module 5), to see this run.
Why Nginx, Not a Hand-Rolled Proxy
Lesson 1’s Node.js proxy works, but it’s missing everything a battle-tested reverse proxy actually needs at scale: efficient handling of tens of thousands of concurrent connections, built-in HTTPS termination, response caching, request buffering, and years of production hardening against edge cases a small hand-written proxy hasn’t encountered yet. Nginx (and alternatives like Caddy or Traefik) exist precisely because “write your own reverse proxy” is rarely the right amount of effort to spend, when a mature, extremely well-tested one is free and widely deployed.
A Basic Nginx Configuration
server {
listen 80;
server_name api.example.com;
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;
}
}
listen 80 accepts incoming HTTP connections, server_name matches this configuration block to a specific domain, location / matches every path, forwarding it to the Node.js application running on localhost:3000, exactly Lesson 1’s xfwd: true, but the explicit, standard way Nginx configuration expresses it, each proxy_set_header line adding one of the forwarding headers directly.
Load Balancing with an Upstream Block
upstream notes_api {
server localhost:3001;
server localhost:3002;
server localhost:3003;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://notes_api;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
An upstream block names a group of backend servers, here, three instances of the same application, likely started by the cluster module or PM2 (Course 2 of this module), Nginx distributes requests across them, round robin by default, exactly the concept Module 8 builds a hand-rolled version of, in a real deployment, Nginx’s built-in load balancing is almost always what’s actually used instead.
Reloading Configuration Without Downtime
sudo nginx -t
sudo nginx -s reload
nginx -t tests a configuration file for syntax errors before applying it, catching a mistake before it takes a live proxy down, nginx -s reload applies configuration changes without dropping existing connections, exactly the zero-downtime property Module 5 covered for application deploys, applied here to the proxy layer itself.
Try It
- Install Nginx on your own machine (or a VM), and configure it to reverse-proxy to a local Node.js application on port 3000.
- Confirm the forwarding headers,
X-Real-IP,X-Forwarded-For, arrive correctly at the Node.js application, by loggingreq.headersin a route. - Configure an
upstreamblock with two or three instances of the same application (started on different ports), and confirm requests are distributed across them. - Run
nginx -tagainst a configuration file with a deliberate syntax error, and read the error message it produces.
Recap
- Nginx is what most real deployments actually use for reverse proxying, mature, efficient, and hardened in ways a hand-rolled proxy (Lesson 1) isn’t, without needing to reinvent it.
- A basic configuration forwards requests to a Node.js application, setting the same forwarding headers Lesson 1’s Node.js proxy added manually.
- An
upstreamblock load balances across multiple backend instances, the same concept Module 8 builds by hand, usually handled by Nginx directly in a real deployment.
Next lesson: HTTPS and certificates, what’s actually happening when a connection is encrypted, and how Nginx terminates it.