Never expose Node directly to the internet. A reverse proxy terminates TLS, serves static files without waking the event loop, buffers slow clients so a 3G upload does not hold a Node connection open, and balances across the cluster. nginx 75 (stable 1.30.5, mainline 1.31.6) is the usual choice; Caddy 7,400 and HAProxy 6,072 do the same job.
upstream bookshelf { server 127.0.0.1:3000; keepalive 32; }
server {
listen 443 ssl;
server_name api.example.com;
gzip on;
gzip_types application/json application/javascript text/css;
location /assets/ { root /srv/bookshelf/public; expires 1y; }
location / {
proxy_pass http://bookshelf;
proxy_http_version 1.1; # needed for keep-alive and WebSocket upgrades
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}nginx is not installed on the machine used for this book, so this configuration is shown as code and was not executed here.
The two X-Forwarded-* headers are what Express 24,430 cares about, and it ignores them until you declare the proxy trustworthy with app.set('trust proxy', 1). With that set, req.ip and req.ips come from X-Forwarded-For starting at the first untrusted address, req.protocol reflects X-Forwarded-Proto, and req.hostname reflects X-Forwarded-Host. Use the smallest true value: 1 for one proxy, 'loopback' when nginx shares the host, an explicit subnet behind a cloud load balancer. true trusts the whole chain, so any client can forge X-Forwarded-For and walk past the IP-keyed rate limiter of Rate Limiting; leaving it off is as bad the other way, lumping every client into the proxy's own address. Headers and Proxies covers the parsing.