Sysadmin

Nginx Reverse Proxy: Complete Configuration Guide 2026

Nginx Reverse Proxy: Complete Configuration Guide 2026

An Nginx reverse proxy receives client HTTP/HTTPS requests and forwards them to internal backend servers. To configure Nginx reverse proxy, you define a server block with the proxy_pass directive, managing SSL, headers, and rate limiting to shield your applications. When I configured the infrastructure for a public healthcare organization with 2,000 endpoints, the main problem was not server power. The real challenge was exposing internal applications without inviting attacks. 73% of breaches start from stolen credentials or exploits on exposed applications, according to the Verizon DBIR 2025. Complying with frameworks like NIS2 art.21 requires strict perimeter protection. Without a reverse proxy, your backend receives direct traffic with no filter. Nginx solves this problem. It handles SSL encryption, absorbs DDoS traffic, logs the real client IP, and caches responses. In this guide, you will learn how to configure Nginx reverse proxy step by step, with production-ready examples to secure your perimeter.

Prerequisites for Nginx Reverse Proxy Setup

You need a Linux server with root access. I tested this configuration on Ubuntu 24.04 LTS, but it works on any Debian-based distribution. You need a backend listening on a local port, for example a Node.js app on port 3000 or a Python app on port 8000. You also need a domain pointed to the server IP to test SSL with Let’s Encrypt.

Core Configuration: Server Blocks and Headers

A reverse proxy sits in front of your application servers. Clients never talk directly to Node.js, Python, or PHP. They talk to Nginx, which forwards the request to the backend. This adds a fundamental security layer. According to W3Techs (2025), Nginx serves over 34% of active websites. If you use it only as a static web server, you waste 90% of its capabilities. When you configure Nginx reverse proxy, you centralize SSL, caching, rate limiting, and load balancing in a single point. The backend stays isolated on the internal network.

Install Nginx on Ubuntu/Debian

The installation requires two commands. No external repositories needed.

sudo apt update
sudo apt install nginx -y

Verify the service starts correctly.

sudo systemctl enable nginx
sudo systemctl start nginx
nginx -v

If you see the version, Nginx is ready. Remove the default server block to avoid conflicts.

sudo rm /etc/nginx/sites-enabled/default

First Server Block and proxy_pass

Create a configuration file for your domain. Use the sites-available directory and create a symbolic link in sites-enabled.

sudo nano /etc/nginx/sites-available/example.com

Insert the base configuration. This block listens on port 80 and serves as the entry point for traffic.

server {
    listen 80;
    server_name example.com;
    location / {
        proxy_pass http://localhost: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;
    }
}

This is the minimum file to make an Nginx reverse proxy work. Enable the configuration and reload.

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

The proxy_pass directive defines where Nginx sends the request. If you have a Node.js app on port 3000, proxy_pass http://localhost:3000; forwards all traffic there. You can point to an internal IP, a UNIX socket, or an upstream server group. If the backend takes more than 60 seconds to respond, Nginx returns a 504 Gateway Timeout. Adjust timeouts for slow applications.

proxy_connect_timeout 10s;
proxy_read_timeout 120s;
proxy_send_timeout 120s;

HTTP Headers: X-Real-IP, X-Forwarded-For

Without these headers, the backend sees the internal Nginx IP as the source. Application logs become useless for tracking attacks. X-Real-IP passes the original client IP. X-Forwarded-For adds the client IP to the proxy chain. X-Forwarded-Proto tells the backend if the original request was HTTPS. Always set them.

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;

Advanced Features: SSL, Load Balancing, and Rate Limiting

Managing SSL on the backend is complex and resource intensive. With Nginx reverse proxy, you terminate encryption at the perimeter. Traffic between Nginx and the backend travels unencrypted on the local network. Install Certbot to automate Let’s Encrypt certificates.

sudo apt install certbot python3-certbot-nginx -y

Obtain the certificate. Certbot modifies your server block automatically.

sudo certbot --nginx -d example.com

Automatic renewal is already configured. Verify with a dry run.

sudo certbot renew --dry-run

If you have two or more instances of the same application, Nginx distributes the traffic. Define an upstream block outside the server block.

upstream backend_node {
    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
    server 10.0.0.3:3000;
}

In your server block, change proxy_pass to point to the upstream.

proxy_pass http://backend_node;

By default, Nginx uses round-robin. If a server has more resources, assign it a higher weight. server 10.0.0.1:3000 weight=3;

Repeated requests saturate the backend CPU. An NGINX Inc. benchmark (2024) shows that caching reduces backend load by up to 70% and halves response times. Configure a cache zone in the http context.

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m;

In your location block, activate the cache.

location / {
    proxy_cache my_cache;
    proxy_pass http://backend_node;
    add_header X-Cache-Status $upstream_cache_status;
}

Rate limiting blocks brute force attacks and prevents API abuse. Define a limit zone based on the client IP.

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

Apply the limit in the location block of your APIs. The burst parameter allows controlled spikes.

location /api/ {
    limit_req zone=api burst=20 nodelay;
    proxy_pass http://backend_node;
}

If a client exceeds 10 requests per second, Nginx returns a 429 Too Many Requests error.

Change the target of proxy_pass based on your stack. For Node.js, the app listens on 3000. proxy_pass http://127.0.0.1:3000; For Python with Gunicorn, use a UNIX socket for better performance. proxy_pass http://unix:/run/gunicorn.sock; For PHP-FPM, do not use proxy_pass. Use the fastcgi_pass directive.

location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

Common Errors and Troubleshooting

502 Bad Gateway: Nginx cannot reach the backend. Check that the app is running and the port or UNIX socket is correct.

504 Gateway Timeout: The backend takes too long to respond. Increase proxy_read_timeout or optimize the application code.

Infinite redirect loop: This happens if the backend redirects to HTTPS but does not read the X-Forwarded-Proto header. Ensure the application recognizes that the original traffic is secure.

Server IP in app logs: You forgot proxy_set_header X-Real-IP $remote_addr;. Add it and configure the app to read the header.

Debugging a proxy that does not forward traffic: Check error logs in /var/log/nginx/error.log. Run sudo nginx -t to verify syntax. Ensure the firewall allows traffic on the listening port and SELinux or AppArmor do not block the connection to the backend.

WebSockets: Add upgrade headers in the location block. WebSockets require a persistent connection that Nginx must keep alive.

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Conclusions with Operational Takeaways

A backend exposed directly is an easy target. Nginx reverse proxy isolates your applications, handles encryption, and filters malicious traffic. The three fundamental steps to secure your infrastructure are: configure proxy_pass to hide the backend, set up SSL termination with Certbot to encrypt incoming traffic, and activate limit_req to block API abuse. Do not wait for the first attack to protect your servers. For more details on proxy directives, consult the official Nginx reverse proxy documentation (https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/). [Read also: SSH Hardening on Linux: Practical Guide 2026]

Share this article:

Written by

Rosario Giordano

Rosario Giordano is a system administrator and IT consultant specializing in cybersecurity and cloud, with over 20 years of experience managing enterprise Linux infrastructures. His areas of expertise include SSH hardening, Kubernetes platforms, PostgreSQL databases, VMware/ Proxmox virtualization, and compliance with NIS2 and ISO 27001 security frameworks