Nginx Reverse Proxy: Setup with SSL on Linux

If you run multiple apps on a single Linux server, an Nginx reverse proxy is how you expose them all cleanly under one IP. Instead of opening a separate port per app, Nginx sits in front and routes requests to the right backend based on domain name or URL path. It also handles SSL in one place, which means free HTTPS for everything behind it. This guide covers the full setup from install to production-ready config with Let’s Encrypt certificates.

Nginx reverse proxy setup SSL Linux server configuration
Nginx sits in front of your apps, handles SSL termination, and routes traffic to the right backend based on hostname or path.

What a Reverse Proxy Actually Does

A reverse proxy sits between clients and your backend apps. Users connect to Nginx on port 80 or 443. Nginx forwards the request to the right app on a local port like 3000 or 8080, then returns the response. The client never talks to the app directly.

This gives you several things at once. SSL termination happens in one place. You can run multiple apps on one server without opening multiple public ports. Nginx also adds caching, security headers, rate limiting, and load balancing across backend instances. For most Linux servers running web apps, an Nginx reverse proxy is the standard setup.

Nginx handles this efficiently because of its event-driven architecture. A single worker process manages thousands of concurrent connections with minimal memory. That makes it much more efficient as a front-end proxy than thread-based servers like Apache.

Step 1: Install Nginx

Install from the official repositories for your distribution:

# Ubuntu / Debian
apt update && apt install nginx -y

# RHEL / AlmaLinux / Rocky Linux
dnf install nginx -y
systemctl enable --now nginx

Check it is running and test the default config before touching anything:

systemctl status nginx
nginx -t

You should see syntax is ok and test is successful. If not, check /var/log/nginx/error.log for the problem before going further.

Step 2: Configure a Basic Reverse Proxy

Nginx reverse proxy configuration file Linux proxy_pass backend
Each server block handles one domain. Nginx routes requests to the backend based on server_name.

Create a config file for your app. On Ubuntu and Debian use /etc/nginx/sites-available/. On RHEL-based systems use /etc/nginx/conf.d/. Replace app.example.com with your domain and 3000 with your app port:

server {
    listen 80;
    server_name app.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;
    }
}

On Ubuntu and Debian, enable it with a symlink then reload:

ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

On RHEL-based systems the file in conf.d/ loads automatically, so just test and reload:

nginx -t && systemctl reload nginx

Step 3: Add Free SSL with Let’s Encrypt

Once the basic proxy works over HTTP, add SSL. Let’s Encrypt provides free certificates that renew automatically. Install Certbot first:

# Ubuntu / Debian
apt install certbot python3-certbot-nginx -y

# RHEL / AlmaLinux
dnf install certbot python3-certbot-nginx -y

Run Certbot for your domain. It edits your Nginx config automatically and sets up HTTPS:

certbot --nginx -d app.example.com

Certbot asks for your email, accepts terms, then asks about HTTP to HTTPS redirects. Choose option 2 to redirect. After it finishes, your site runs on HTTPS with a valid certificate. Test automatic renewal:

certbot renew --dry-run
systemctl status certbot.timer

The timer renews certificates before they expire. Confirm it shows active.

Step 4: Proxy Multiple Apps on One Server

Each app gets its own server block with its own domain name. Nginx routes traffic to the right backend based on the hostname in the request. Create a separate config file per app:

# /etc/nginx/sites-available/api
server {
    listen 80;
    server_name api.example.com;
    location / {
        proxy_pass         http://127.0.0.1:4000;
        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;
    }
}

# /etc/nginx/sites-available/dashboard
server {
    listen 80;
    server_name dashboard.example.com;
    location / {
        proxy_pass         http://127.0.0.1:5000;
        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;
    }
}

Enable both and get certificates for both domains in one command:

ln -s /etc/nginx/sites-available/api /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/dashboard /etc/nginx/sites-enabled/
certbot --nginx -d api.example.com -d dashboard.example.com
nginx -t && systemctl reload nginx

If you run Ollama on the same server, this is also how you expose it with a domain and SSL. See our guide on installing Ollama on Linux for the full setup, then point a server block at port 11434.

Step 5: Add Security Headers

Nginx reverse proxy security headers HTTPS SSL Linux production
Security headers protect users from common browser-based attacks. Add them once in Nginx and they apply to every backend automatically.

Security headers protect users against clickjacking, content injection, and protocol downgrade attacks. Add them once in a shared snippet and include it in every server block:

# Create the snippet
cat > /etc/nginx/snippets/security-headers.conf << 'EOF'
add_header X-Frame-Options           "SAMEORIGIN"           always;
add_header X-Content-Type-Options    "nosniff"              always;
add_header X-XSS-Protection          "1; mode=block"        always;
add_header Referrer-Policy           "strict-origin"        always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
EOF

Include it in each server block:

server {
    listen 443 ssl;
    server_name app.example.com;
    include snippets/security-headers.conf;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

After reloading Nginx, check your headers at securityheaders.com. A properly configured server scores an A or A+.

Step 6: Rate Limiting and WebSocket Support

Rate limiting protects your backends from brute force and abuse. Add limit zones in the http block of /etc/nginx/nginx.conf, then apply them to specific locations:

# In nginx.conf http block
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m  rate=1r/s;

# In your server block
location /     { limit_req zone=general burst=20 nodelay; proxy_pass http://127.0.0.1:3000; }
location /login { limit_req zone=login  burst=5  nodelay; proxy_pass http://127.0.0.1:3000; }

For WebSocket connections, standard proxy_pass is not enough because WebSockets require an HTTP upgrade. Add these two extra headers to any WebSocket location:

location /ws {
    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_read_timeout 86400s;
}

Quick Reference: Nginx Commands

nginx -t                          # Test config syntax
systemctl reload nginx            # Reload without dropping connections
systemctl restart nginx           # Full restart
tail -f /var/log/nginx/error.log  # Watch error log
tail -f /var/log/nginx/access.log # Watch access log

Conclusion

An Nginx reverse proxy is one of the most practical setups you can add to a Linux server. Free SSL for every app, clean domain routing, security headers in one place, and rate limiting across all backends. Once the pattern is set up, adding a new app takes under five minutes. If you want to harden the server that runs Nginx before it goes live, read our guide on Linux server hardening to lock down SSH, configure the firewall, and set up fail2ban first.

}