Hosting & VPS Guides

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.

By CheckDomainHealth Editorial Team Reviewed by Dionis Ceban Updated Jun 28, 2026 10 min read Intermediate

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

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.

Reverse proxy flow
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

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.

Install Nginx
sudo apt update
sudo apt install nginx -y
Check status and firewall
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.

Example reverse proxy server block
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.

Certbot for Nginx
# 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:

HTTPS reverse proxy example
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.

WebSocket proxy 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
Check listening ports
sudo ss -tulpn

If the app must listen on all interfaces, restrict public access with firewall rules or provider security groups.

Why this matters

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.

  1. DNS — Confirm domain points to VPS.
  2. Nginx config — Run nginx -t before reload.
  3. App status — Confirm app is running on the expected local port.
  4. Firewall — Confirm 80 and 443 are open.
  5. SSL — Confirm certificate covers the domain.
  6. Redirects — Check HTTP to HTTPS and www/non-www behavior.
  7. Proxy headers — Confirm app sees correct protocol and host.
  8. 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.

Run SSL Check →

Common problems

502 Bad Gateway

High

Nginx cannot reach the upstream application.

Next step: Check app status, port, proxy_pass target and firewall.

Redirect loop

High

Nginx, 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

Medium

The 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

High

Certificate does not cover requested hostname.

Next step: Issue certificate for root and www as needed.

WebSockets not working

Medium

Upgrade headers are missing.

Next step: Add Upgrade and Connection headers.

Wrong visitor IP in app logs

Medium

The 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

Medium

Nginx location rules or app base URL are wrong.

Next step: Check paths, asset URLs and Nginx locations.

Large uploads fail

Medium

Nginx upload size limit is too low.

Next step: Set client_max_body_size as needed.

Certbot validation fails

Medium

DNS, firewall or Nginx server block is not ready.

Next step: Check A records, ports 80/443 and server_name.

Nginx reload fails

High

Configuration syntax is invalid.

Next step: Run nginx -t, fix errors and reload again.

How to configure Nginx reverse proxy safely

  1. Step 1: Confirm DNS points to VPS

    Check A/AAAA records for the domain.

  2. Step 2: Start the application locally

    Confirm the app runs on a known port such as 127.0.0.1:3000.

  3. Step 3: Install Nginx

    Install and confirm Nginx is running.

  4. Step 4: Create server block

    Configure server_name and proxy_pass.

  5. Step 5: Pass proxy headers

    Set Host, X-Real-IP, X-Forwarded-For and X-Forwarded-Proto.

  6. Step 6: Test Nginx config

    Run nginx -t before reloading.

  7. Step 7: Enable SSL

    Use Certbot after HTTP proxy works.

  8. Step 8: Configure redirects

    Choose one canonical HTTPS hostname.

  9. Step 9: Check logs

    Review Nginx and application logs.

  10. Step 10: Secure backend port

    Keep the app local or restrict public access.

Useful Nginx reverse proxy commands

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.

Use these free tools to verify your configuration after applying changes.

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.

Get Help Run Domain Health Check

Was this guide helpful?

Your feedback helps us improve our guides for everyone.