Nginx SSL and Reverse Proxy Setup on a VPS
Practical guide to using Nginx as an HTTPS reverse proxy on a VPS for Node.js, Python, Docker, APIs, dashboards and web applications.
Introduction
Nginx is commonly used on VPS servers as a reverse proxy. Instead of exposing an application directly to the internet, Nginx receives public HTTP/HTTPS traffic and forwards requests to an internal application running on another port.
This setup is useful for Node.js apps, Python apps, Docker containers, APIs, admin dashboards and services that should not listen publicly. Nginx can handle SSL, redirects, headers, compression, request limits and routing while the application runs privately behind it.
Quick answer
To set up Nginx SSL reverse proxy on a VPS, point DNS to the server, run the application on a local/internal port, configure an Nginx server block with proxy_pass, pass correct proxy headers, install an SSL certificate with Certbot, redirect HTTP to HTTPS, test Nginx config, reload Nginx and verify status, SSL and headers.
Nginx SSL and reverse proxy on a VPS
An Nginx reverse proxy sits between visitors and your application.
Visitor request:
https://example.com
Nginx receives:
Public HTTPS request on port 443
Nginx forwards to:
Internal app such as http://127.0.0.1:3000
Application responds:
Nginx sends the response back to the visitor
The application does not need to manage public SSL directly. Nginx can terminate HTTPS and proxy traffic to the app.
When to use Nginx reverse proxy
Use Nginx as a reverse proxy when your application runs on an internal port or service.
Common use cases:
- Node.js application on port 3000
- Python app with Gunicorn/Uvicorn
- Docker container exposed locally
- API backend
- admin dashboard
- WebSocket app
- frontend app with backend API
- multiple apps on one VPS
- app that should not be publicly exposed by port
- SSL termination in front of internal services
A reverse proxy lets you expose clean public URLs while keeping application ports private.
Before setup
Before configuring Nginx, confirm the basics.
You need:
- VPS with root or sudo access
- domain DNS pointing to VPS IP
- Nginx installed
- application running locally
- internal app port known
- firewall allowing 80 and 443
- firewall not exposing app port publicly unless needed
- SSL method ready, usually Certbot
- access to application logs
- rollback access to server
Do not start with SSL if the plain HTTP reverse proxy does not work yet. First make Nginx proxy to the app, then add HTTPS.
Basic reverse proxy flow
Public request:
https://example.com
Nginx listens on:
80 and 443
Application listens on:
127.0.0.1:3000
Nginx forwards:
location / {
proxy_pass http://127.0.0.1:3000;
}
Result:
Visitors use example.com, while the app remains behind Nginx.
Using 127.0.0.1 keeps the app accessible only locally unless other firewall or bind settings expose it.
Install Nginx
On Ubuntu/Debian, install Nginx from the package manager.
sudo apt update
sudo apt install nginx -y
systemctl status nginx
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status
Test in browser: http://your-server-ip
If Nginx is working, you should see the default Nginx page or your configured site.
Example Nginx reverse proxy config
Create a server block for your domain.
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
}
}
# Enable site:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
# Test config:
sudo nginx -t
# Reload Nginx:
sudo systemctl reload nginx
Replace example.com and port 3000 with your real domain and application port.
Add SSL with Certbot
After the HTTP reverse proxy works and DNS points correctly, issue SSL.
# Install Certbot:
sudo apt install certbot python3-certbot-nginx -y
# Issue certificate:
sudo certbot --nginx -d example.com -d www.example.com
# Test renewal:
sudo certbot renew --dry-run
# Check certificates:
sudo certbot certificates
Certbot can automatically update the Nginx config for HTTPS and redirects. Always test Nginx config after changes.
HTTPS reverse proxy config
A simplified HTTPS reverse proxy config may look like this:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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 https;
}
}
This is a simplified example. Real configs may need WebSocket headers, timeouts, upload limits, caching rules or app-specific paths.
Proxy headers explained
Proxy headers help the application understand the original request.
Important headers:
- Host: original domain requested by visitor
- X-Real-IP: visitor IP
- X-Forwarded-For: chain of client/proxy IPs
- X-Forwarded-Proto: original protocol, usually https
- X-Forwarded-Host: original host, if needed
Without correct headers, the application may generate wrong URLs, log only proxy IPs, create redirect loops or think the request is HTTP instead of HTTPS.
WebSocket support
Apps using WebSockets need upgrade headers.
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 https;
}
WebSocket issues often appear as real-time features failing while normal pages still load.
App binding and firewall
For security, the internal app should usually bind to localhost.
Better
- 127.0.0.1:3000 — app reachable only locally through Nginx
Riskier
- 0.0.0.0:3000 — app may be exposed publicly on the network
sudo ss -tulpn
If the app must listen on all interfaces, restrict public access with firewall rules or provider security groups.
Why this matters
Nginx reverse proxy setup matters because many VPS applications should not be exposed directly to the internet. Nginx provides a stable public entry point, handles HTTPS, forwards requests to the app, and helps control headers, redirects and routing.
A wrong reverse proxy config can cause 502 errors, redirect loops, broken WebSockets, wrong visitor IPs, SSL warnings or insecure public app ports.
How to check Nginx reverse proxy setup
Use SSL Checker, Website Status Checker, HTTP Header Checker and server logs.
- DNS — Confirm domain points to VPS.
- Nginx config — Run nginx -t before reload.
- App status — Confirm app is running on the expected local port.
- Firewall — Confirm 80 and 443 are open.
- SSL — Confirm certificate covers the domain.
- Redirects — Check HTTP to HTTPS and www/non-www behavior.
- Proxy headers — Confirm app sees correct protocol and host.
- Logs — Check Nginx error logs and application logs.
Check your SSL certificate
Use SSL Checker to verify certificate coverage, HTTPS response and SSL configuration after Nginx reverse proxy setup.
Common problems
502 Bad Gateway
HighNginx cannot reach the upstream application.
Next step: Check app status, port, proxy_pass target and firewall.
Redirect loop
HighNginx, CDN or app redirects conflict, often around HTTP/HTTPS.
Next step: Set X-Forwarded-Proto correctly and review app trusted proxy settings.
App port exposed publicly
MediumThe backend app is reachable directly instead of only through Nginx.
Next step: Bind app to 127.0.0.1 or restrict port with firewall.
SSL certificate mismatch
HighCertificate does not cover requested hostname.
Next step: Issue certificate for root and www as needed.
WebSockets not working
MediumUpgrade headers are missing.
Next step: Add Upgrade and Connection headers.
Wrong visitor IP in app logs
MediumThe app sees Nginx IP instead of real client IP.
Next step: Pass X-Real-IP and X-Forwarded-For, then configure trusted proxies in the app.
Static files not loading
MediumNginx location rules or app base URL are wrong.
Next step: Check paths, asset URLs and Nginx locations.
Large uploads fail
MediumNginx upload size limit is too low.
Next step: Set client_max_body_size as needed.
Certbot validation fails
MediumDNS, firewall or Nginx server block is not ready.
Next step: Check A records, ports 80/443 and server_name.
Nginx reload fails
HighConfiguration syntax is invalid.
Next step: Run nginx -t, fix errors and reload again.
How to configure Nginx reverse proxy safely
-
Step 1: Confirm DNS points to VPS
Check A/AAAA records for the domain.
-
Step 2: Start the application locally
Confirm the app runs on a known port such as 127.0.0.1:3000.
-
Step 3: Install Nginx
Install and confirm Nginx is running.
-
Step 4: Create server block
Configure server_name and proxy_pass.
-
Step 5: Pass proxy headers
Set Host, X-Real-IP, X-Forwarded-For and X-Forwarded-Proto.
-
Step 6: Test Nginx config
Run nginx -t before reloading.
-
Step 7: Enable SSL
Use Certbot after HTTP proxy works.
-
Step 8: Configure redirects
Choose one canonical HTTPS hostname.
-
Step 9: Check logs
Review Nginx and application logs.
-
Step 10: Secure backend port
Keep the app local or restrict public access.
Useful Nginx reverse proxy commands
Check Nginx config:
sudo nginx -t
Reload Nginx:
sudo systemctl reload nginx
Restart Nginx:
sudo systemctl restart nginx
Check Nginx status:
systemctl status nginx
Check listening ports:
sudo ss -tulpn
Check Nginx error log:
sudo tail -100 /var/log/nginx/error.log
Check access log:
sudo tail -100 /var/log/nginx/access.log
Check website headers:
curl -I https://example.com
Follow redirects:
curl -IL http://example.com
Test local app:
curl -I http://127.0.0.1:3000
Commands are examples. Replace example.com and port 3000 with your real domain and app port.
Multiple apps on one VPS
Nginx can proxy different domains or paths to different apps.
Examples:
- app1.example.com → 127.0.0.1:3000
- app2.example.com → 127.0.0.1:4000
- api.example.com → 127.0.0.1:8000
- example.com/api → 127.0.0.1:5000
For beginners, subdomain-based routing is often cleaner than path-based routing because apps may not handle subpaths correctly.
CDN and proxy notes
If using Cloudflare or another CDN in front of Nginx, check:
- SSL mode
- origin certificate
- real visitor IP handling
- proxy headers
- redirect rules
- cached assets
- WebSocket support
- firewall allowing CDN IPs if restricted
- /.well-known/acme-challenge/ for Certbot validation
Redirect loops often happen when CDN SSL mode, Nginx redirects and app HTTPS settings are not aligned.
Security checklist
Security checklist
Use this checklist after configuring Nginx as an HTTPS reverse proxy.
Only 80 and 443 public unless needed
Avoid exposing unnecessary application ports.
Backend app port not exposed
Bind the app to localhost where possible.
SSL certificate valid
Confirm certificate covers the public hostname.
HTTP redirects to HTTPS
Use one canonical HTTPS URL.
Nginx config tested before reload
Run nginx -t before applying changes.
App logs reviewed
Check for upstream and redirect errors.
Nginx logs reviewed
Review error and access logs after changes.
Security updates applied
Keep Nginx and the OS updated.
Upload limits configured
Set client_max_body_size if uploads are needed.
Rate limits considered for public APIs
Protect APIs from abuse where appropriate.
Trusted proxy settings configured in app
Let the app trust Nginx forwarded headers.
Certbot renewal tested
Run certbot renew --dry-run after SSL setup.
Backups configured
Back up Nginx config and application data.
Frequently asked questions
What is an Nginx reverse proxy?
It is a server setup where Nginx receives public requests and forwards them to an internal application or service.
Why use Nginx in front of my app?
Nginx can handle SSL, redirects, public ports, headers and routing while the app runs privately behind it.
What does proxy_pass do?
proxy_pass tells Nginx where to forward requests, such as http://127.0.0.1:3000.
Why do I get 502 Bad Gateway?
Nginx cannot reach the upstream app. The app may be stopped, using a different port or blocked.
Do I need SSL in the app too?
Usually Nginx handles public SSL, while the internal app can run over local HTTP. Some setups may require end-to-end TLS.
Why does my app redirect in a loop?
The app may not know the original request was HTTPS. Set proxy headers and trusted proxy settings.
Can I host multiple apps behind one Nginx server?
Yes. Use separate server blocks, subdomains or carefully designed path routing.
Related tools
Use these free tools to verify your configuration after applying changes.
Related guides
Browse all Hosting & VPS guides →Need help applying this fix?
Send us your domain, report link or issue details. CheckDomainHealth will review the request and route it to the right technical team if hands-on support is needed.
Was this guide helpful?
Your feedback helps us improve our guides for everyone.
Thanks for your feedback!