Understanding Reverse Proxy with Nginx

When building modern web applications, especially microservices or full-stack setups (like React frontend + Node backend), you'll often need a reverse proxy. One of the most popular tools for this is Nginx. In this blog, we’ll break down what a reverse proxy is, how Nginx does it, and how it routes traffic to different ports — with examples and clear concepts.
What is a Reverse Proxy?
A reverse proxy sits in front of your servers and routes client requests to the appropriate backend service. Unlike a forward proxy, which works on behalf of a client, a reverse proxy works on behalf of the server.
Think of it as:
Client ➝ NGINX (Reverse Proxy) ➝ Backend Services (like Node.js, Python, etc.)
Why Use a Reverse Proxy?
Single point of entry: You expose only one domain (say xyz.com) to the outside world.
Port hiding: Backend services often run on different ports (like 3000, 4000), but the client only sees port 80 or 443.
Load balancing: Nginx can distribute requests across multiple servers.
SSL termination: Nginx can manage HTTPS encryption/decryption.
Static + dynamic serving: You can serve frontend files and proxy API calls using one tool.
Example Setup
Install Nginx on your machine then →
Nginx Configuration for Reverse Proxy
Let’s look at a slightly more advanced case routing based on domain name.
Imagine you're running two Node.js apps on ports 3000 and 3001 inside a single EC2 instance. You want:
Your Nginx listens on port 80, and based on the domain, proxies to the correct app.
Here’s how the architecture looks:
Here’s the Nginx configuration:
# /etc/nginx/sites-available/xyz.com
server {
listen 80;
server_name xyz.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;
}
}
# /etc/nginx/sites-available/abc.com
server {
listen 80;
server_name abc.com;
location / {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
How Does This Work?
When a client (like a browser or Postman) sends a request to your server (Nginx on port 80), here’s what happens under the hood:
1. Domain Matching (Server Name)
Nginx inspects the Host header in the incoming request — this is essentially the domain (e.g., xyz.com or abc.com).
If the request is for
xyz.com, Nginx matches theserver_namexyz.comblockIf it’s for
abc.com, it matches theserver_nameabc.comblock
This is called virtual hosting, and it lets you run multiple websites on the same server.
2. Reverse Proxying to Correct Port
Once the domain is matched, Nginx proxies the request to the appropriate Node.js server running on a specific port:
xyz.com➝ Nginx ➝localhost:3000abc.com➝ Nginx ➝localhost:3001
The client never sees these internal ports — it only talks to port 80 (or 443 for HTTPS).
Example Flow:
Browser sends request to
http://xyz.com/Nginx sees the domain is
xyz.com, matches the first server blockNginx forwards the request to
http://localhost:3000Your Node.js app handles it and returns the response
Nginx passes that back to the browser
Tips & Best Practices
Use
proxy_passwith care: Don’t forget trailing slashes (/api/vs/api) — they behave differently.Use HTTPS: You can set up SSL using Let’s Encrypt + Certbot easily.
Use separate location blocks: Keep proxy and static locations separate to avoid conflicts.
Watch out for CORS: Nginx won’t solve CORS errors alone — configure CORS on the backend.