What Happens When You Type a URL and Press Enter
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →What a reverse proxy does, why almost every production app sits behind one, and copy-ready Nginx configs for Node.js apps, WebSockets, static files, upload limits and load balancing.
Look at almost any production web app and you'll find something sitting in front of it: Nginx, a load balancer, a CDN. That something is a reverse proxy. Understanding what it does explains a lot of mysterious behaviour, like 502 errors, wrong client IPs and WebSockets that refuse to connect.
A reverse proxy receives requests from the internet and forwards them to one or more servers behind it, then passes the responses back. The client only ever talks to the proxy.
Browser -> Nginx (port 443) -> Your app (127.0.0.1:3000)
Why put something in front of your app?
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;
}
}
Those headers matter. Without them, your app thinks every request comes from 127.0.0.1 over plain HTTP. Then client IPs in logs are wrong, rate limiting by IP limits everyone at once (rate limiting), and redirect logic may loop because the app doesn't know the request was HTTPS.
Tell your framework to trust the proxy so it reads these headers. In Express, for example: app.set('trust proxy', 1).
After installing a certificate (Certbot can edit this config for you), you'll have a listen 443 ssl block and a redirect from port 80:
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
WebSockets need the HTTP upgrade headers passed through, or connections fail:
location /socket/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 1h; # keep long-lived connections open
}
(WebSockets vs SSE vs polling.)
location /assets/ {
alias /var/www/app/public/assets/;
expires 1y;
add_header Cache-Control "public, immutable";
}
Hashed asset files can be cached for a year. (Caching strategies.)
Nginx's default request body limit is small (1MB). Uploads larger than that fail with 413 before your app sees them:
client_max_body_size 20M;
Set it to the largest upload you actually allow. (File upload guide.)
gzip on;
gzip_types text/plain text/css application/json application/javascript image/svg+xml;
gzip_min_length 1024;
server {
server_name api.example.com;
location / { proxy_pass http://127.0.0.1:4000; }
}
server {
server_name example.com;
location / { proxy_pass http://127.0.0.1:3000; }
}
upstream app_servers {
least_conn;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
server {
location / {
proxy_pass http://app_servers;
}
}
Run two app instances, and you can restart them one at a time for zero-downtime deploys. Keep the app stateless (sessions in a database or Redis, not memory) so any instance can serve any request. (Scaling a web app.)
| Symptom | Likely cause |
|---|---|
| 502 Bad Gateway | App isn't running, crashed, or is on a different port |
| 504 Gateway Timeout | App too slow; check slow queries or external calls |
| 413 Request Entity Too Large | client_max_body_size too low |
| WebSocket fails to connect | Missing Upgrade/Connection headers |
| Redirect loop on HTTPS | App doesn't trust X-Forwarded-Proto |
| All users share one IP in logs | Missing forwarded headers or trust proxy setting |
Always validate config before reloading:
sudo nginx -t && sudo systemctl reload nginx
And check the error log when something's wrong: sudo tail -f /var/log/nginx/error.log. (Status codes explained.)
A server that receives client requests and forwards them to backend servers, returning their responses. It commonly handles HTTPS, static files, load balancing and request limits.
A forward proxy acts on behalf of clients, for example a company proxy for browsing. A reverse proxy acts on behalf of servers, sitting in front of them to receive incoming traffic.
Nginx couldn't get a valid response from the app behind it, usually because the app isn't running, crashed, or is listening on a different address or port than proxy_pass points to.
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →There are dozens of HTTP status codes, but about fifteen cover nearly everything. What each one means, when to return it, and the mix-ups (401 vs 403, 301 vs 302) that confuse everyone.
Read →A, AAAA, CNAME, MX, TXT and NS records in plain English, why DNS changes seem slow, and the troubleshooting commands that tell you what is really going on.
Read →