Step By Step on How I Setup NGINX Reverse Proxy to Hide Port 3000 on AWS EC2
Few days ago, I made a big mistake — I left Port 3000 open to the world on my EC2. Anyone could access http://my-ip:3000 directly. That is dangerous because hackers can attack your Node.js process directly. Today, I fixed it properly with NGINX Reverse Proxy. After this fix, my MERN app now runs on clean; http://IP without any port number, and port 3000 is completely private.
What is Reverse Proxy? (Simple beginner explanation)
Imagine your EC2 is a big office building.
Your Node.js app is a boss sitting in Room 3000 inside. Before, you allowed visitors to go straight to Room 3000. That is risky.
Reverse Proxy is like putting a professional receptionist (NGINX) at the main entrance (Port 80). All visitors must come to reception. Receptionist will go inside, collect what you need from Room 3000, and bring it to the visitor. No visitor ever enters Room 3000 directly.
That is what the diagram above shows.
User -> NGINX (Port 80/443) -> Node.js (Private Port 3000). Direct access to 3000 is BLOCKED by Security Group.
Why Every Production App Uses This?
- Security: Node.js is not built to face internet directly. NGINX handles attacks better.
- Clean URL: Users don't need to remember :3000. Just your IP or domain.
- HTTPS Ready: You can easily add free SSL Certificate (Let's Encrypt) to NGINX later for https://
- Performance: NGINX serves frontend static files (React build) faster than Node.
Step-by-Step Implementation
Step 1: Install NGINX on EC2; ssh into your EC2 and run
sudo apt update
sudo apt install nginx -y
sudo systemctl start nginx
check if it works: open http://Your_EC2_IP, you should see ''Welcome to nginx'' page.
Step 2: Configure Reverse Proxy
This is the main step. We tell NGINX: "Any request that comes to Port 80, forward it to localhost:3000".
Open the default config:
sudo nano /etc/nginx/sites-available/default
Select all (Ctrl+K) and delete, then paste this clean config:
server {
listen 80;
server_name your_domain_or_EC2_IP;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Save; Ctrl+0, Enter, Ctrl+X
Step 3: Test and Restart NGINX
Always test before restarting to aviod crashing
sudo nginx -t
If it says ''syntax is ok, test is successful'', then;
sudo systemctl restart nginx
Step 4: The Security Fix (Most Important)-- Close Port 3000 in AWS
This is what i forgot yesterday, now fix it:
Go to AWS Console > EC2 > Security Groups >Select your SG> Edit Inbound Rules:
Delete the rule for Port 3000. Keep only:
- SSH (22) from My IP
- HTTP (80) from Anywhere
- HTTPS (443) from Anywhere
Save rules
Now check: http://YOUR_IP:3000 should should not work, "Not reachable or connection refused" Good! (BLOCKED) ✅
But http://YOUR_IP should show your MERN app working ✅ Good! That means NGINX is protecting you.
If both work like that, you are now production-ready
Common Error I Faced
After setup, I got 502 Bad Gateway. Reason? My Node app had crashed and was not running on 3000. Fix: I used PM2 to keep it alive:
- pm2 start server.js --name mern-app
- pm2 save
- pm2 startup
Then NGINX started working.
Why This Is Best Practice?
- Security — No direct attack on Node.js
- Clean URL — No need to type :3000
- Ready for HTTPS — You can add free SSL (Let's Encrypt) to NGINX later
- Faster — NGINX serves static files faster than Node
Conclusion
From yesterday's vulnerability to today's secure setup, this is growth. Don't expose your app ports directly. Always put NGINX in front.
Read Previous Post: I explained the mistake that led to this fix here: My First EC2 Mistake: I Left Port 3000 Open and Almost Got Hacked — https://sirvick-deployments.blogspot.com/2026/09/my-first-ec2-mistake-i-left-port-3000.html

Comments
Post a Comment