NGINX

NGINX basics. How to create redirects

by ,


We have by far the largest RPM repository with dynamic stable NGINX modules and VMODs for Varnish 4.1 and 6.0 LTS. If you want to install NGINX, Varnish, and lots of useful modules for them, this is your one-stop repository to get all performance-related software.
You have to maintain an active subscription in order to be able to use the repository!

Adopting NGINX as your webserver may be a more challenging task if you come to it after Apache.

One of the primary reasons for that is how different things are in the two webservers. URL redirects are done quite differently in Apache and NGINX.

However, it is quite easy to comprehend how redirects are to be done in NGINX when you know some basics:

  • The rewrite or location directives in NGINX are matched against the URI without arguments. That is, for a URL https://example.com/foo?bar=1, these directives operate on /foo and don’t “see” the query part
  • The $request_uri is the NGINX variable that holds the complete URI, e.g. /foo?bar=1

In NGINX, you may end up writing redirects with these directives: rewrite, location, return, if, map. Sounds like too many?
Fear not. There are just a handful of cases to review. Which directives you will use depends on what kind of URLs you are redirecting (whether they use query params), and their number.

Redirecting a single URL irrespective of original arguments

In the simplest case, you want to add a simple redirect from one URL to another, and you don’t bother about the query parameters from the original URL.
The most efficient approach to this would be using the exact matching of the location directive.

To redirect /foo to /bar permanently, you would use:

location = /foo {
    return 301 /bar;
}

What we’ve done there is created an exact location that matches requests for /foo.
A location NGINX is best to be understood as a request’s context.

Exact matching (note equals sign) means that NGINX will match requests to /foo only, and not /foomore.

Throughout request processing, NGINX may “jump” from one context to another, but only a single specific context will be eventually chosen for making up a response.
If a context ends up with a return statement, no more context switching will take place and NGINX will ultimately serve a redirect when either 301 (permanent) or 302 (temporary) status codes are used as the first argument to return.

With our configuration, requests to /foo will be redirected to /bar with the permanent redirect status 301.

Remember, any kind of location matching in NGINX is done against the URI without/irrespective of its arguments.
So the same location will match the URI /foo?bar=1&... or /foo?bar=2&..., etc..
A visitor will be redirected to /bar.

To preserve original URL arguments in the new URL, you will use $args variable in the new URL:

location = /foo {
    return 301 /bar$is_args$args;
}

Now /foo?bar=1 will redirected to /bar?bar=1, etc.

When to use exact matching for redirects:

  • You want to redirect a single or a few URLs
  • You want to redirect URLs for any value of request arguments in the original URL

Redirecting many URLs irrespective of their arguments

If you have dozens and dozens of redirects of the previous type, you may want to leverage the map directive.
It comes in handy when you deal with array-like structures in NGINX.

The map directive actually defines a new NGINX variable based on the values of other variables.
For the task of redirecting many URLs, we can simply define a $redirect_to variable based on $uri, and perform a redirect when it isn’t empty.

Remember, the map directive should go directly within http {} context, and not within server {}.

http {

    map $uri $redirect_to {
        default "";
        /foo     /bar;
        /lorem  /ipsum;
        /one    /two;
    }

    # ...
}

Now that we have defined our variable, it can be used in the server block:

server {
    server_name example.com
    if ($redirect_to != "") {
        return 301 $redirect_to;
    }
}

Now, visitors to /foo are redirected to /bar.
Requests to /lorem are redirected to /ipsum.
Requests to /one are redirected to /two.

It’s easy to add many redirects by adding them as new entries under the $redirect_to map.

Using rewrite

The rewrite also helps you in batch redirecting many URLs to many URLs.

If you have clear conformity between the old and new URL sets, you will want to use rewrite for redirects.

Suppose that you had URLs of type /<some.php> using a bunch of PHP files.
Now you have switched to a front-controller CMS framework, where index.php bootstrap file handles SEO URLs.

For /some.php, you want to redirect to /index.php?page=some. The rewrite comes in very handy because it supports regular expressions:

location / 
    rewrite ^(.*) /index.php?page=$1 permanent;
}

location = /index.php {
   fastcgi_pass ...
}

Note how we have to add prefixed location / and exact located /index.php in order to exclude /index.php itself from the redirect.
When NGINX sees a request for URI /index.php, it will immediately switch to location = /index.php.

The result is:

  • /foo.php redirected to /index.php?page=foo
  • /bar.php redirected to /index.php?page=bar
  • etc. etc.

The rewrite directive requires specifying permanent (permanent redirect) or redirect (temporary redirect) for specifying the type of the desired redirect.

Redirecting a single URL, accounting to its arguments

When you must match the original URL with its arguments, it is easiest to use if and evaluate the value of $request_uri inside it:

if ($request_uri = "/foo?bar=1") {
    return 301 /bar;
}

Redirecting many URLs, accounting to their arguments

Say you had a URL structure that heavily relied on URL parameters, and decided to switch to SEO-friendly URLs. If there is no clear way to define which old URLs correspond to the new URLs, you can use map approach again.

The only difference to the previous approach is that we are going to use $request_uri variable which includes URL arguments:

http {

    map $request_uri $redirect_to {
        default "";
        "/foo?arg1=val1"  /bar;
        /lorem               /ipsum;
        /one                 /two;
    }

    # ...
}

Now that we have defined our variable, it can be used in the server block:

server {
    server_name example.com
    if ($redirect_to != "") {
        return 301 $redirect_to;
    }
}

Note that /foo will not be redirected, because we now match against complete URI.
Only visits to /foo?arg1=val1 are redirected to /bar.

Requests to /lorem are redirected to /ipsum.
Requests to /one are redirected to /two.
If parameters are added to those URLs, they will not be redirected.

Redirects in NGINX are powerful

Web redirects in NGINX are not complicated and can be done in a very efficient manner when you know which case you’re dealing with.

NGINX has the power to map one variable to another, it has regular expression support, different location matching types, and many built-in variables.

All this requires some learning curve but the result is the rewarding, fast webserver operation.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: