A reverse proxy sits between the internet and your application server. When someone visits your website, they talk to the reverse proxy, which forwards the request to your actual app and sends the response back.
Your app never talks directly to the internet. The reverse proxy handles that.
Why use a reverse proxy?
Without a reverse proxy:
Internet β Your Node.js app (port 3000)
Your app handles everything: SSL, compression, static files, rate limiting, and your actual business logic. Thatβs a lot of responsibility for one process.
With a reverse proxy (Nginx):
Internet β Nginx (port 80/443) β Your Node.js app (port 3000)
Nginx handles the boring stuff (SSL, compression, static files, caching). Your app just handles business logic.
What a reverse proxy does
| Feature | Without proxy | With Nginx |
|---|---|---|
| SSL/HTTPS | Your app handles it | Nginx handles it |
| Static files | Your app serves them | Nginx serves them (much faster) |
| Compression | Your app compresses | Nginx compresses |
| Rate limiting | You build it | Nginx does it |
| Load balancing | Not possible | Nginx distributes traffic |
| Multiple apps | One app per port | Multiple apps on port 80 |
Basic Nginx reverse proxy
server {
listen 80;
server_name myapp.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Now myapp.com (port 80) forwards to your Node.js app on port 3000. Users never see port 3000.
Multiple apps on one server
# app1.com β port 3000
server {
listen 80;
server_name app1.com;
location / {
proxy_pass http://localhost:3000;
}
}
# app2.com β port 4000
server {
listen 80;
server_name app2.com;
location / {
proxy_pass http://localhost:4000;
}
}
# api.app1.com β port 5000
server {
listen 80;
server_name api.app1.com;
location / {
proxy_pass http://localhost:5000;
}
}
Three different apps, all on port 80, distinguished by domain name.
Load balancing
upstream backend {
server localhost:3001;
server localhost:3002;
server localhost:3003;
}
server {
listen 80;
server_name myapp.com;
location / {
proxy_pass http://backend;
}
}
Nginx distributes requests across three instances of your app. If one crashes, traffic goes to the other two.
When you need a reverse proxy
Yes:
- Running a Node.js/Python/Go app in production
- Need SSL/HTTPS
- Serving static files alongside an API
- Running multiple apps on one server
- Need rate limiting or caching
No:
- Using a PaaS (Vercel, Heroku, Railway) β they handle this for you
- Local development
- Serverless functions (Lambda, Cloud Functions)
Common reverse proxies
| Tool | Best for |
|---|---|
| Nginx | Most popular, great for static files + proxy |
| Caddy | Automatic HTTPS, simpler config than Nginx |
| Traefik | Docker/Kubernetes, auto-discovers services |
| HAProxy | High-performance load balancing |
| Apache | Legacy, still widely used |
For the full Nginx config reference, see the Nginx cheat sheet.