Nginx as a Reverse Proxy: A Practical Guide
Why You Need a Reverse Proxy
If you write a Node.js API (using Express) or a Python backend (using FastAPI), you might be tempted to simply deploy it to an AWS EC2 instance, open port 80 to the world, and let users connect directly to your Node server. This is a terrible idea.
Application servers like Node and Python are brilliant at executing business logic, but they are notoriously terrible at handling raw internet traffic. They struggle with slow HTTP requests (like a user on a 3G network slowly uploading an image), they consume too much memory serving static files (like massive React JS bundles), and they are easily overwhelmed by basic DDoS attacks.
The solution is to place a Reverse Proxy—like the legendary open-source Nginx server—in front of your application. Nginx is an absolute tank. It handles millions of concurrent connections with mere megabytes of RAM. It sits on port 80 (HTTP) and 443 (HTTPS), acts as a shield, filters out bad traffic, handles the complex SSL/TLS cryptography, and only forwards the clean, legitimate requests to your fragile Node.js server running safely hidden on local port 3000.
Basic Proxy Configuration
Configuring Nginx is done via blocks in the /etc/nginx/nginx.conf file. To proxy traffic from a public domain to your local app, you define a server block.
server {
listen 80;
server_name api.mywebsite.com;
location / {
# Forward all traffic to the local Node.js app
proxy_pass http://127.0.0.1:3000;
# Pass the original client IP to Node.js
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Because Nginx is proxying the request, your Node.js app thinks every request is coming from 127.0.0.1 (Nginx itself). The proxy_set_header directives are crucial; they inject the real user's IP address into the headers so your Node app knows who is actually connecting (crucial for rate limiting and logging).
Serving Static Files and Load Balancing
If you are serving a React or Vue frontend, do not let Node.js serve those files. Let Nginx do it. Nginx can serve static HTML, CSS, and images thousands of times faster than Express.js.
server {
listen 80;
server_name mywebsite.com;
# Nginx serves the React frontend blazingly fast
location / {
root /var/www/my-react-app/build;
try_files $uri /index.html;
}
# Nginx forwards only the API calls to the backend
location /api/ {
proxy_pass http://127.0.0.1:3000;
}
}
As your company scales, a single Node server won't be enough. Nginx seamlessly transforms into a Load Balancer using an upstream block. You simply define three local ports (3000, 3001, 3002), spin up three instances of your Node app, and Nginx will flawlessly distribute the incoming traffic across all three, instantly tripling your application's capacity.