URL Rewrites and Redirects

Introduction

Sometimes URLs change—marketing renames a path, you drop .html suffixes, or force www to non-www. Nginx handles this with return redirects and rewrite. This chapter shows when to use each, how to avoid redirect loops, and common patterns for canonical hostnames.

Prerequisites

return for Redirects (Preferred for Simple Cases)

Permanent (301)

nginx
location /old-page {
    return 301 /new-page;
}

Temporary (302)

nginx
location /maintenance {
    return 302 /status.html;
}

Full URL redirect

nginx
return 301 https://example.com$request_uri;

Code explanation:

  • 301 — browsers and search engines cache the new URL
  • return stops processing—fast and clear

HTTP → HTTPS (Again)

nginx
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

Pick one canonical host (example.com vs www.example.com).

www to non-www (or Reverse)

Certbot can issue cert covering both names with -d example.com -d www.example.com.

rewrite Directive

nginx
rewrite ^/blog/(.*)$ /news/$1 permanent;

Flags:

FlagEffect
permanent301 redirect
redirect302 redirect
lastRestart location search with new URI (internal)
breakStop rewrite processing in current location

Internal rewrite (no browser redirect):

nginx
location / {
    rewrite ^/user/(\d+)$ /profile?id=$1 last;
}

Code explanation:

  • Browser still shows /user/123
  • Nginx internally reprocesses as /profile?id=123
  • Backend must understand rewritten URI

Warning

Redirect Loops

rewrite or return pointing to a URL that triggers the same rule again causes too many redirects. Test with:

bash
curl -I -L http://example.com/old-page

rewrite vs return

UseTool
Simple redirect to new pathreturn 301 ...
Complex pattern capturerewrite ... permanent
Internal path change onlyrewrite ... last or break
HTTPS / canonical hostreturn 301 https://...

Prefer return when a redirect is all you need—easier to read and slightly faster.

Trailing Slash Normalization

Add slash to directories:

nginx
rewrite ^([^.]*[^/])$ $1/ permanent;

Or per-location:

nginx
location /docs {
    return 301 /docs/;
}

Trailing slash rules interact with proxy_pass—test API paths carefully.

Remove .html Extension (Legacy Sites)

nginx
location / {
    try_files $uri $uri.html $uri/ =404;
}

Or external redirect:

nginx
rewrite ^/(.*)\.html$ /$1 permanent;

Map-Based Redirects (Many URLs)

nginx
map $request_uri $new_uri {
    default "";
    /old-a    /new-a;
    /old-b    /new-b;
}
 
server {
    if ($new_uri != "") {
        return 301 $new_uri;
    }
}

Warning

if Is Evil in Nginx

Small if for redirects is common; complex logic in if breaks unexpectedly. Prefer map or separate location blocks when rules grow.

Example: Legacy Path Migration

FAQ

301 vs 302?

301 permanent (SEO passes link equity). 302 temporary (A/B tests, short moves).

Can rewrite proxy to backend?

Use rewrite ... break then proxy_pass in same block—advanced; easier to fix backend routes when possible.

Query string preserved?

return 301 /new$request_uri; includes path + query. rewrite with $1 may drop query unless you append $is_args$args.

How many redirects are OK?

One hop ideal; chain hurts performance and SEO—consolidate rules.

Certbot redirect vs my return?

Do not duplicate HTTP→HTTPS blocks—keep Certbot’s or yours, not both fighting.

Log redirects?

Access log shows status 301/302 and target—use for verifying migration traffic.