The single most common Nginx job: an app (Node, Python/Gunicorn, Go, Rails…) listens on a local port, and Nginx sits in front of it on port 80/443. The trap is forgetting the forwarded headers — without them the app logs Nginx’s IP and thinks every request is plain HTTP.
server {
listen 80;
server_name 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;
proxy_set_header X-Forwarded-Host $host;
proxy_read_timeout 60s;
}
}
Key points:
proxy_pass http://127.0.0.1:3000;— note no trailing slash here. With no path on the target and no slash, Nginx forwards the original URI unchanged. (Trailing-slash behaviour matters when you proxy a subpath — see the subpath recipe.)Host $host— passes the browser’s hostname through, so virtual hosting and absolute redirects in your app work.X-Forwarded-For/X-Real-IP— the real client IP. Configure your framework to trust these (e.g. Expressapp.set('trust proxy', 1), DjangoSECURE_PROXY_SSL_HEADER).X-Forwarded-Proto $scheme— tells the app whether the browser used HTTPS, even though Nginx talks to it over plain HTTP. Frameworks use this to buildhttps://URLs and set Secure cookies.
Putting the repeated
proxy_set_headerlines in a file like/etc/nginx/proxy_paramsandinclude proxy_params;inside the location keeps every proxy block consistent.
Apply it:
sudo nginx -t && sudo nginx -s reload