Site migrations, canonical domains and clean URLs all need redirects, and return is simpler and safer than rewrite.
return stops processing and sends a response:
return 301 https://example.com$request_uri;: permanent redirect (308 also preserves the method and body).return 302 /maintenance.html;: temporary redirect.return 403;orreturn 200 "ok";for a direct response.
rewrite regex replacement [flag]; changes the URI using regex captures:
- With no flag, NGINX continues with the next rewrite and then re-searches locations for the new URI.
last: stop rewrites here and search locations again with the new URI.break: stop rewrites and keep processing in the current location.redirect(302) andpermanent(301): send a redirect to the client. A replacement starting withhttp://orhttps://also redirects.
Prefer return whenever the target doesn't need a regex; it is clearer and cheaper.
Common variables: $host (Host header or server name), $request_uri (original URI with query string), $uri (normalized, possibly rewritten, without query string), $args and $arg_name for the query string, $scheme, $remote_addr and $http_<header> for any request header.
The map directive computes one variable from another and is the clean way to handle many redirects: keep a table of old to new paths instead of dozens of rewrites.
Example
map $uri $new_uri { # in http context
/old-pricing /pricing;
/blog/2023 /blog/archive/2023;
}
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri; # canonical host
}
server {
listen 80;
server_name example.com;
if ($new_uri) { return 301 $new_uri; } # table-driven redirects
rewrite ^/users/(\d+)$ /profile?id=$1 last; # internal rewrite
location = /profile { return 200 "profile $arg_id\n"; }
}
# GET /users/42 -> profile 42Remember
- return for redirects and direct responses; rewrite only when you need regexes
- last re-searches locations; break stays in the current one
- $request_uri keeps the query string; $uri doesn't
Watch out for
- Redirecting with $uri instead of $request_uri and losing the query string.