Reverse proxy with proxy_pass

NGINX · Practical · 14 min

Putting NGINX in front of an application is the most common NGINX job, and header or path mistakes break apps subtly.

proxy_pass forwards a request to a backend and returns its response. The URI sent upstream follows a precise rule:

  • Without a URI part (proxy_pass http://127.0.0.1:8000;) the original request URI is passed unchanged: /api/users stays /api/users.
  • With a URI part, even just / (proxy_pass http://127.0.0.1:8000/;), the part matching the location prefix is replaced by it: in location /api/, /api/users becomes /users.

By default NGINX sends Host as the upstream address and adds nothing about the client, so the app sees NGINX as the client. Pass the original information explicitly:

  • 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; so apps generate https URLs.

The application must be configured to trust these headers only from the proxy. Timeouts (proxy_connect_timeout, proxy_read_timeout, default 60s) govern how long NGINX waits; when the backend is down or times out the client gets a 502 or 504. Upstream errors and their reasons appear in the error log.

Example

server {
    listen 80;
    server_name example.com;

    location /api/ {
        proxy_pass http://127.0.0.1:8000/;      # /api/users -> /users
        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_read_timeout 30s;
    }

    location /admin/ {
        proxy_pass http://127.0.0.1:9000;       # /admin/x -> /admin/x
    }
}
# curl -i http://example.com/api/health
# HTTP/1.1 502 Bad Gateway   <- when nothing listens on :8000

Remember

  • proxy_pass without URI keeps the path; with a URI it replaces the location prefix
  • Forward Host, X-Forwarded-For and X-Forwarded-Proto explicitly
  • 502 = backend unreachable, 504 = backend timeout

Watch out for

  • Adding or forgetting a trailing slash in proxy_pass and getting doubled or missing path prefixes.

Check yourself

  1. In location /app/ { proxy_pass http://b:3000/; }, what path does the backend see for /app/login?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass