Nginx Reverse Proxy for Beginners

Beginner
12 min read
Updated: Nov 2026
5 steps
Ubuntu / EC2

What Is a Reverse Proxy?

A reverse proxy is a front door. Visitors talk to Nginx on port 80 (or 443). Nginx then asks your app—often Node.js or Express on 127.0.0.1:3000—for the page, and sends the answer back.

  • Public port: Nginx on 80/443.
  • Private port: your app on localhost only.
  • Directive: proxy_pass chooses the upstream.
Beginner tip.

This walkthrough uses Ubuntu-style commands (common on Amazon Linux alternatives and Ubuntu EC2 AMIs). Package names can differ slightly on Amazon Linux—use the same ideas.

In Simple Words

What problem does it solve?

EC2 deploy demos often bind Node to port 80 with elevated privileges. A reverse proxy is the cleaner habit: Node on 3000, Nginx on 80, visitors never type :3000.

When should you use it?

After a working app from Node on EC2, Express on EC2, React on EC2, or Next.js on EC2. If you prefer a managed public door, study an Application Load Balancer instead.

A tiny traffic picture

Browser → server:80 (Nginx) → proxy_pass → 127.0.0.1:3000 (Node) → response back through Nginx.

Official docs.

See the Beginner’s Guide (simple proxy) and the proxy module reference for every proxy_* directive.

Words You Will See

WordPlain meaning
proxy_passForward matching requests to this upstream URL
proxy_set_headerPass Host, real client IP, and other headers to the app
sites-availableUbuntu folder for server configs (enabled via symlink)
nginx -tTest config syntax before reload
502 Bad GatewayNginx could not get a valid answer from the upstream
upstreamOptional named block listing one or more backends

What You Need First

  • SSH access to a Linux server (often an EC2 instance with an Elastic IP).
  • A Node/Express (or similar) app already running on port 3000 via PM2 or node. Confirm with curl http://127.0.0.1:3000 on the server.
  • Security group inbound rules for TCP 22 (SSH) and TCP 80 (HTTP). You can close public 3000 once the proxy works.
Change the port if needed.

If your app listens on 8080 or 4000, use that port in every proxy_pass and curl below.

Step-by-Step

Five small steps. After Step 5, http://YOUR_PUBLIC_IP/ should show your app.

Step 1 — Install Nginx

On Ubuntu / Debian-style AMIs:

ssh
sudo apt update
sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx

Open the public IP in a browser. You should see the default Nginx welcome page.

How It Works

  1. apt installs the Nginx package and a default site on port 80.
  2. enable starts Nginx after reboot.
  3. If the welcome page fails, check security group port 80 before editing configs.

Step 2 — Confirm Your App on Port 3000

Nginx can only proxy to something that answers.

ssh
curl -I http://127.0.0.1:3000
# or
pm2 status

You want an HTTP response (often 200), not “Connection refused.”

How It Works

  1. 127.0.0.1 means “this same machine,” which is what proxy_pass will use.
  2. If curl fails, fix the app first (see Node on EC2 or Express on EC2).
  3. Prefer binding the app to localhost in production so only Nginx talks to it publicly.

Step 3 — Create a Reverse Proxy Site

Add a simple server block that forwards everything to Node.

ssh
sudo nano /etc/nginx/sites-available/my-app
/etc/nginx/sites-available/my-app
server {
    listen 80;
    server_name _;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
        proxy_cache_bypass $http_upgrade;
    }
}

How It Works

  1. listen 80 is the public HTTP door.
  2. proxy_pass sends the request to your Node process.
  3. Header lines help the app see the original host and client IP.
  4. Upgrade / Connection lines help WebSocket-style upgrades (handy for some Next.js / realtime apps).

Step 4 — Enable the Site and Reload

Disable the default site if it conflicts, then enable yours.

ssh
sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

How It Works

  1. sites-enabled is what Nginx actually loads (symlink pattern on Ubuntu).
  2. nginx -t must say the syntax is ok before reload.
  3. reload applies the new config with less downtime than a full restart.
Amazon Linux note.

You may edit /etc/nginx/conf.d/my-app.conf instead of sites-available. Same server { } content; still run nginx -t and reload.

Step 5 — Test from Your Browser

  1. Open http://YOUR_PUBLIC_IP/ (or your domain if DNS already points here).
  2. You should see the same app that answered on port 3000.
  3. Optional: remove public security-group access to port 3000 so only Nginx is the door.
  4. If you get 502, jump to Common Mistakes below.

How It Works

  1. The browser only knows port 80 now.
  2. Nginx forwards to localhost:3000 and returns the body and status.
  3. Later upgrades: HTTPS (certificates), or move the public door to an ALB + ACM.

Common Beginner Mistakes

502 because Node is down

Run pm2 status and curl 127.0.0.1:3000 on the server before blaming Nginx.

Wrong proxy_pass port

Config says 3000 but the app listens on 8080. Match them exactly.

Skipped nginx -t

A missing semicolon can leave the old site running or block reload. Always test first.

Default site still wins

Remove or disable default in sites-enabled so your proxy config is the one that answers.

Port 80 closed in the security group

SSH works, browser times out. Open TCP 80 (and 443 later).

App still bound only to a public demo on 80

Stop the old “listen on 80 with sudo” process so Nginx can own port 80.

Key Takeaways

  • Nginx on 80/443, app on a high localhost port.
  • proxy_pass is the core reverse-proxy line.
  • Test the app locally, then test Nginx, then the public URL.
  • nginx -t then reload after every config change.

One line: install Nginx, proxy_pass to your app, test, reload, then browse port 80.

Frequently Asked Questions

Nginx listens on the public port (usually 80 or 443), receives each visitor request, asks your backend (for example Node on 127.0.0.1:3000) for the response, and returns that response to the visitor.
On Linux, ports below 1024 are privileged. Demos sometimes use sudo with PM2. A reverse proxy lets Node stay on a normal high port while Nginx owns 80/443.
proxy_pass tells Nginx which upstream URL should handle matching requests. A common beginner line is proxy_pass http://127.0.0.1:3000;
Nginx reached the proxy target but got no healthy response. Usually the Node process is down, listening on a different port, or blocked. Check PM2 status and curl localhost:3000 on the server.
No. Start with HTTP on port 80. Add TLS later with a certificate (on AWS you may use ACM on an ALB, or certificates on the instance). Get the reverse proxy working first.
No. This guide runs Nginx on your server. An ALB is a managed AWS load balancer in front of instances. Both can forward traffic; pick one public door for your architecture.

Need an app to proxy?

Deploy a simple Node site on EC2 first, then come back and put Nginx in front.

Node.js on EC2 →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

10 people found this page helpful