Nginx is an extremely efficient and quite flexible web server. When you want to do a redirect in Nginx, you have a few options to select from, so you can choose the one that suits you best to do an Nginx redirect.

Simplest and fastest: return

The by far simplest and fastest – because there is no regexp that has to be evaluated – is to use the return statement. Place this in your server block:

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

This is a permanent redirect. Use 302 if you want a temporary redirect. A full sample server block could be:

server {
    listen 80;
    listen [::]:80;
    hostname example.com www.example.com;
    return 301 https://example.com$request_uri;
}

[bjornad]

Using regular expressions

If you need something more complex, don’t be afraid to use a regexp – they’re still extremely fast in Nginx:

rewrite ^/foo/(bar)/(.*)$ https://$server_name/$1/$2 permanent;

Replace permanent with redirect if you want a temporary (HTTP 302) redirect. Our full sample server block could then be:

server {
    listen 80;
    listen [::]:80;
    hostname example.com www.example.com;
    root /var/www/example.com/public;
    rewrite ^/foo/(bar)/(.*)$ $scheme://$server_name/$1/$2 permanent;
}

Using maps

If you have a list of URLs or regular expressions that you want to redirect differently, you ought to look into using a map, which you very well may define in a separate file for your convenience. Just note that the map definition must be outside the server block:

include redirect-map.conf;
server {
    […]
    if ( $redirect_uri ) {
        return 301 $redirect_uri;
    }
}

The file redirect-map.conf may then look something like this:

map $request_uri $redirect_uri {
    /about.html          /about-us;
    /customers.html      /our-customers;
    /products.html       /our-products;
}

Note the following excerpt from the docs:

A regular expression should either start from the “~” symbol for a case-sensitive matching, or from the “~*” symbols (1.0.4) for case-insensitive matching. A regular expression can contain named and positional captures that can later be used in other directives along with the resulting variable.

Here’s an example that might help you understand what that was about:

map $request_uri $redirect_uri {
    /about.html          /about-us;
    /customers.html      /our-customers;
    /products.html       /our-products;
    # Match any url that ends in products.html or producs.htm
    ~products\.html?$    /our-products;
    # case-insensitive version of the above
    ~*products\.html?$   /our-products;
    # A named capture that maps
    # e.g. product-1234.html into /products/item-1234/overview
    ~product-(?<sku>\d+)\.html   /products/item-$sku/overview;
}

Cute, huh? :-) Also please note that the variable name $redirect_uri have no special meaning: It is one I made up. You can name it whatever you like, but make sure the variable name in the map and the server block matches.

[bjornad]

Some useful variables

I’ve used a few of these in the examples above, so you might have noticed them before. These are variables that comes predefined by Nginx, ready for you to use in your configs: $scheme – The scheme used for the current request. E.g. “http” or “https” $host – The hostname provided by the client for the current request. $server_name – The first hostname from the hostname declaration in your config for the server block that responds to the request. $request_uri – The full original request URI – with arguments. $request_filename – The file path for the current request.

Here’s also the full list of predefined variables available in your Nginx config.

Some useful recipes for an Nginx redirect

HTTP to HTTPS

return 301 https://$host$request_uri;

Read more about HTTP to HTTPS redirects in Nginx here.

Canonical hostname

If the hostname doesn’t match the first name in the server_name list. Makes sure your content is only available at the canonical hostname, e.g. to avoid duplicate content issues. Excellent for redirecting non-www to www or redirecting www to non-www in Nginx as long as your server block is only for a single website.

server_name example.com www.example.com example.net www.example.net _;
if ( $host != $server_name ) {
    return 301 $scheme://$server_name$request_uri;
}

Nginx is very efficient, but please note that it would be more efficient to have two separate server blocks – one for the hostnames you want to redirect and one for the website. Then Nginx won’t have to do the comparison for every request.

Generic non-www/www redirects

If your server block covers multiple websites – e.g. a WordPress multisite network and you don’t want all of them to redirect to the same hostname, you can still do a universal check:

Redirect non-www to www:

if ( $host !~ ^www\. ) {
    return 301 $scheme://www.$host$request_uri;
}

Redirect www to non-www

if ( $host ~ ^www\.(?<domain>.+)$ ) {
    return 301 $scheme://$domain$request_uri;
}

For the www/non-www redirects, it is worth mentioning again that using separate server blocks, where one use the return statement described at the top, is by far the most efficient.