When hosting multiple websites on a single server, exposing Docker containers directly (e.g., on :3000) can leave your system vulnerable and cluttered. The cleaner and more secure approach is to hide applications behind Nginx reverse proxy, route traffic using Cloudflare, and block direct IP access so this setup ensures:
- One public IP can serve multiple domains
- Each domain routes to the correct containerized app
- Ports like 3000, 5000, etc. are never exposed to the public
- Traffic is secured and manageable
Step 1: Cloudflare Setup
- Add your domains to Cloudflare.
- Point DNS records (A or CNAME) to your server’s IP and enable Proxy (orange cloud).
- In SSL/TLS settings, use Flexible if your server serves plain HTTP, or Full (strict) if you install certs later.
Step 2: Firewall Configuration
Allow only required ports:
sudo apt install ufw -y sudo ufw allow ssh sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw enable
This ensures only SSH (22), HTTP (80), and HTTPS (443) are open.
Step 3: Block Direct Container Access
Docker often bypasses UFW, exposing container ports. Secure them at the Linux level:
sudo iptables -I DOCKER-USER -p tcp --dport 22 -j ACCEPT sudo iptables -I DOCKER-USER -p tcp --dport 80 -j ACCEPT sudo iptables -I DOCKER-USER -p tcp --dport 443 -j ACCEPT sudo iptables -I DOCKER-USER ! -s 127.0.0.1 -p tcp -j DROP
This ensures only Nginx (on localhost) can reach containers like :3000
.
Step 4: Install and Configure Nginx
Install Nginx:
sudo apt install nginx -y sudo rm -f /etc/nginx/sites-enabled/default
Create a catch-all to block raw IP requests:
server { listen 80 default_server; server_name _; return 444; }
Step 5: Reverse Proxy for Multiple Sites
Example: siteA.com
→ container on 127.0.0.1:3000
.
server { listen 80; server_name siteA.com www.siteA.com; location / { proxy_pass http://127.0.0.1:3000; 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; } }
For siteB.com
→ container on 127.0.0.1:4000
:
server { listen 80; server_name siteB.com www.siteB.com; location / { proxy_pass http://127.0.0.1:4000; 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; } }
Apply changes:
sudo nginx -t sudo systemctl reload nginx
Step 6: Optional — Restrict Access to Cloudflare Only
Add Cloudflare IP ranges to your Nginx server block:
allow 173.245.48.0/20; allow 103.21.244.0/22; # ... (all ranges from https://www.cloudflare.com/ips/) deny all;
This ensures requests bypassing Cloudflare are blocked.
Summary
By combining Docker, Nginx reverse proxy, and Cloudflare, you can:
Serve multiple domains from one server
Keep internal container ports hidden
Block raw IP access
Control traffic cleanly through Cloudflare
This setup improves security, scalability, and makes your hosting environment production-ready.