Skip to main content

Command Palette

Search for a command to run...

Understanding Reverse Proxy with Nginx

Published
•3 min read•View as Markdown
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:

  • xyz.com ➝ routes to app on port 3000

  • abc.com ➝ routes to app on port 3001

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 the server_name xyz.com block

  • If it’s for abc.com, it matches the server_name abc.com block

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:

The client never sees these internal ports — it only talks to port 80 (or 443 for HTTPS).

Example Flow:

  1. Browser sends request to http://xyz.com/

  2. Nginx sees the domain is xyz.com, matches the first server block

  3. Nginx forwards the request to http://localhost:3000

  4. Your Node.js app handles it and returns the response

  5. Nginx passes that back to the browser

Tips & Best Practices

  • Use proxy_pass with 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.