fbpx

NGINX

NGINX basics. How to create redirects

by , , revisited on


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

Adopting NGINX as your web server 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, and 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 the $args variable in the new URL:

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

Now /foo?bar=1 will be 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 the 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 for 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 for 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 the 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 the 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.

Permanent redirect from subdirectory to the root

Suppose that you had a multistore Magento and hosted your language-specific stores in subdirectories.
Eventually, you decided to have the single international version of your website at the root, and eliminate the subdirectories setup.
To preserve the SEO juice, create redirects from the subdirectory URLs to the root like this:

rewrite ^/(?:en-sa|ar-sa|en-qa|ar-qa|en-kw|ar-kw|ru-ru|en-ru)/(.*)$ /$1 redirect;

This redirects /en-sa/product.html to /product.html, etc.

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 web server operation.

  1. Atlas

    Nice, wish if there were more details, like where do you add these lines, on the example.com.conf file? or Nginx conf file?

    which one would it be for this example:
    – all pages from https://example.com/pages
    – redirects to https://example.com

    like, https://example.com/pages/chicken.html to https://example.com/chicken.html

    Thanx

    Reply
    • Danila Vershinin

      Website-specific configuration should be placed to configuration files residing at /etc/nginx/sites-available/, which in turn is symlinked to /etc/nginx/sites-enabled/.
      E.g., for the domain example.com, you would have the /etc/nginx/sites-available/example.com.conf file which is present as a symlink at /etc/nginx/sites-enabled/example.com.conf.
      In nginx.conf, include enabled sites with include sites-enabled/*.conf;. Each website specific configuration will likely have a set of server blocks specific to the domain that matches the filename.
      This is a convention only. Nobody prevents you from putting the entire configuration to nginx.conf, but then it will be hard to manage.

      Whichever way you choose to manage your configuration across the files, redirects configuration must be placed within server {} blocks.
      If such redirects require maps, it is worth noting that map directive can only go to http {} section, outside of server blocks.

      For a permanent redirect you can:

      location ~ ^/pages/(.*)$ {
          return 301 /$1;
      }
      

      Additionally, you may want to optimize it by nesting within prefixed location:

      location /pages/ {
          location ~ ^/pages/(.*)$ {
              return 301 /$1;
          }
      }
      
      Reply
  2. ijamessaxon

    Oh so close…. What about matching on the GET args?

    You’ve got /foo?arg1=val1 but what about /foo?arg1=(.*) or something like that? Either in location or rewrite, is that doable? It seems to ignore it completely.

    Reply
    • Greg

      You’ll need to append args just like below.


      location ~ ^/pages/(.*)$ {
      return 301 /$1$is_args$args;
      }

      <\code>

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

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