← Back to DevBytes

How to Configure Nginx as a Reverse Proxy

What Is a Reverse Proxy?

A reverse proxy is a server that sits between client devices and backend servers, forwarding client requests to the appropriate backend and returning the response to the client as if it came from the proxy itself. Unlike a forward proxy, which protects clients from the internet, a reverse proxy shields backend servers from direct client access, providing a single entry point for all incoming traffic.

Nginx is one of the most popular web servers and excels as a reverse proxy due to its event-driven architecture, low memory footprint, and extensive module ecosystem. Configuring Nginx as a reverse proxy allows you to route traffic based on hostnames, paths, or headers, terminate SSL/TLS, load balance across multiple backends, cache responses, and apply security policies—all with a lightweight, high-performance tool.

Why Use Nginx as a Reverse Proxy

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

How to Configure Nginx as a Reverse Proxy

Prerequisites

You need a server with Nginx installed (version 1.9 or later is recommended for full features like WebSocket proxying). Basic familiarity with Nginx configuration files (/etc/nginx/nginx.conf and /etc/nginx/sites-available/) is assumed. Make sure you have reload permissions (e.g., via sudo).

Basic Reverse Proxy Setup

The core directive is proxy_pass, which tells Nginx where to forward requests. A minimal location block looks like this:

server {
    listen 80;
    server_name example.com;

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

This forwards all requests to example.com to a backend application running on localhost:3000. After saving the configuration, test it with nginx -t and reload Nginx with systemctl reload nginx (or nginx -s reload).

Passing Original Request Headers

By default, Nginx modifies some headers. To preserve the original client IP, protocol, and hostname, add proxy header directives:

location / {
    proxy_pass http://backend_server;
    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;
}

$host passes the original host header, X-Real-IP gives the client IP, X-Forwarded-For appends to a list of proxies, and X-Forwarded-Proto indicates http or https. Most frameworks rely on these headers for logging, redirects, and security checks.

Load Balancing Across Multiple Backends

Define an upstream block to group backend servers, then reference it in proxy_pass. Nginx supports several algorithms: round‑robin (default), least_conn, ip_hash, and random.

upstream app_cluster {
    least_conn;
    server 10.0.0.1:3000 weight=3;
    server 10.0.0.2:3000;
    server 10.0.0.3:3000 backup;
}

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://app_cluster;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

The least_conn method sends requests to the server with the fewest active connections. weight adjusts proportion, and backup marks a server used only when others are down. You can also enable sticky sessions with the ip_hash directive.

SSL Termination and HTTPS Proxy

To serve traffic over HTTPS and forward to backend services (which can remain HTTP), set up a server block listening on port 443 with SSL certificates. Then proxy to the backend using http:// (or https:// if you want encrypted backend communication).

server {
    listen 443 ssl http2;
    server_name secure.example.com;

    ssl_certificate     /etc/ssl/certs/example.crt;
    ssl_certificate_key /etc/ssl/private/example.key;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://app_cluster;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Ssl on;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name secure.example.com;
    return 301 https://$host$request_uri;
}

The X-Forwarded-Proto https tells the backend the original request used HTTPS. The redirect block ensures all plain HTTP requests are upgraded.

WebSocket Proxying

WebSocket connections require the Upgrade and Connection headers. Nginx needs explicit configuration to handle the protocol switch:

location /ws/ {
    proxy_pass http://websocket_backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 86400s;
}

Setting proxy_http_version 1.1 and the Upgrade/Connection headers allows Nginx to establish a persistent connection. A long proxy_read_timeout prevents idle WebSocket connections from being closed prematurely.

Response Caching

Nginx can cache backend responses to serve subsequent requests faster. Define a cache path and key, then enable caching inside a location block.

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

server {
    ...
    location / {
        proxy_cache my_cache;
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_key "$scheme$request_method$host$request_uri";
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        proxy_pass http://app_cluster;
        add_header X-Cache-Status $upstream_cache_status;
    }
}

The proxy_cache_path directive defines storage and the shared memory zone. proxy_cache_valid sets TTLs per status code. proxy_cache_key builds a unique key from request parameters. The X-Cache-Status header helps debugging.

Security Headers and Basic Access Control

Use Nginx’s built‑in modules to add security headers and restrict access before requests reach the backend:

location / {
    # Deny access to hidden files and certain user agents
    if ($request_uri ~* "^/(\.well-known/)?(\.|\_)" ) {
        return 403;
    }

    # Security headers
    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-when-cross-origin" always;

    proxy_pass http://app_cluster;
    proxy_set_header ...;
}

You can also limit request methods, rate-limit with limit_req_zone, and block specific IPs using deny directives in the server context.

Best Practices for Nginx Reverse Proxy Configuration

Conclusion

Configuring Nginx as a reverse proxy unlocks a powerful, efficient layer between your users and backend infrastructure. By mastering proxy_pass, header forwarding, load balancing, SSL termination, and caching, you can build a resilient and secure architecture. The examples above provide a solid foundation, but Nginx’s module system allows for even deeper customization—from dynamic routing with Lua to sophisticated rate limiting. Start with a simple proxy, gradually layer in improvements following the best practices, and you’ll have a production-grade setup that handles traffic with confidence.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles