How to configure Nginx as a reverse proxy

Configuring Nginx as a reverse proxy lets you publish one stable web entrypoint for an application that actually runs elsewhere, so you can keep backend ports private while handling TLS termination, request limits, and logging at the edge.

Nginx matches the incoming host and URI to a server and location block, forwards the request with proxy_pass, and can add or preserve the request headers that most applications need to generate correct external URLs and record client origin. The usual baseline is to pass the public Host plus X-Real-IP, X-Forwarded-For, and X-Forwarded-Proto.

Packaged Linux installs often load multiple server blocks from /etc/nginx/conf.d/ or /etc/nginx/sites-enabled/, so local validation should target the same hostname defined in server_name instead of assuming a bare 127.0.0.1 request will hit the new proxy block. If the upstream uses HTTPS, WebSockets, or long-lived streaming responses, add the protocol-specific proxy directives in the same location rather than treating this basic HTTP example as complete.

Steps to configure Nginx as a reverse proxy:

  1. Confirm the upstream application responds directly on its backend address before routing traffic through Nginx.
    $ curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/
    200

    Use the upstream's real scheme and port. If the backend already fails here, fix it before changing the proxy layer.

  2. Create /etc/nginx/conf.d/app-reverse-proxy.conf with an upstream block and a matching public server block.
    upstream app_backend {
        server 127.0.0.1:8080;
        keepalive 32;
    }
    
    server {
        listen 80;
        server_name example.com;
    
        location / {
            proxy_pass http://app_backend;
    
            proxy_http_version 1.1;
            proxy_set_header Connection "";
    
            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;
    
            proxy_connect_timeout 5s;
            proxy_send_timeout 60s;
            proxy_read_timeout 60s;
        }
    }

    proxy_set_header Host $host keeps the public hostname for the upstream, while proxy_http_version 1.1 plus proxy_set_header Connection "" keeps upstream keepalive working consistently on packaged Nginx builds that still default to HTTP/1.0 for proxied requests.

    The proxy_pass target above has no URI suffix, so Nginx forwards the original request URI. If you add a URI to proxy_pass or proxy only a subpath such as /app/, Nginx rewrites the location-matched prefix differently.

  3. Test the updated Nginx configuration before reloading the daemon.
    $ sudo nginx -t
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
  4. Reload Nginx so the reverse proxy block starts serving requests for the public hostname.
    $ sudo systemctl reload nginx

    Use sudo nginx -s reload when the daemon is running without systemd, such as in some containers or hand-started test environments.

  5. Request the public host through Nginx and confirm that the upstream receives the forwarded request metadata.
    $ curl -s -H 'Host: example.com' http://127.0.0.1/
    Host: example.com
    X-Real-IP: 127.0.0.1
    X-Forwarded-For: 127.0.0.1
    X-Forwarded-Proto: http

    From another workstation, use curl --resolve example.com:80:127.0.0.1 http://example.com/ or test the real DNS name directly so the request matches server_name.

    If the packaged Nginx welcome page still answers instead, the request did not match this server block. Check server_name, disable the conflicting default site if necessary, or retest with an explicit Host header.

  6. Inspect the Nginx access log for the proxied request.
    $ sudo tail -n 2 /var/log/nginx/access.log
    127.0.0.1 - - [09/Apr/2026:13:19:00 +0000] "GET / HTTP/1.1" 200 90 "-" "curl/8.5.0"

    A successful request for the expected hostname confirms that the reverse proxy block served the traffic. If the application logs forwarded headers itself, compare both log streams during the same test window.