Requests and static files
Return redirects and errors
Use explicit responses and redirects, preserve methods when required, and avoid redirect loops.
8 minute lesson
Nginx can answer a request without reading a file or contacting an upstream. The return directive produces the response directly, and it’s the right tool for redirects, health checks, and hard rejections.
server {
listen 80;
server_name www.example.com;
location = /health {
return 200 "ok\n";
}
location = /old-pricing {
return 301 /pricing;
}
location = /promo-2025 {
return 302 /pricing;
}
}
The /health location answers with a body and no filesystem access at all. Monitoring systems can hit it thousands of times a day without touching your application.
For redirects, the status code is a real decision. 301 means permanent: browsers and search engines cache it, often aggressively, and there is no practical way to un-teach a browser that has seen it. 302 means temporary and stays revisitable. My advice is to ship a 302 first, watch it behave for a few days, then upgrade to 301 only when the mapping is stable.
There is a second axis: the request method. With 301 and 302, most clients resend the follow-up request as a GET, even if the original was a POST. Use 307 (temporary) or 308 (permanent) when the request method must be preserved — an API endpoint that moved, for example, where turning a POST into a GET would silently break clients.
Verify status and destination with curl -I:
curl -I http://www.example.com/old-pricing
# HTTP/1.1 301 Moved Permanently
# Location: /pricing
curl -s http://www.example.com/health
# ok
Then check the destination doesn’t bounce back. Redirect loops happen when the target location itself redirects — often through a rewrite rule or an HTTPS redirect added later in another block. Follow the chain end to end:
curl -sIL -o /dev/null -w "%{num_redirects} redirects -> %{url_effective}\n" http://www.example.com/old-pricing
# 1 redirects -> http://www.example.com/pricing
More than one hop deserves a look. Twenty hops means a loop: curl gives up with Maximum (50) redirects followed, and browsers show “too many redirects”.
Add a temporary old-path redirect and a plain /health response to your test server. Inspect status and Location headers with curl -I before choosing permanent caching.
Lesson completed