A good[ish] website

Web development blog, loads of UI and JavaScript topics

Understanding Nginx location directive and redirects

Filed under: Server— Tagged with: nginx, redirect, regex

The Nginx location directive sets configuration depending on a request URI, on which, for example, redirects can be performed.

The basics

Here's a really simple, but working, Nginx server configuration with one location block, that block performs a redirect to a different domain:

server {
  root /var/www/;

  location /foo {
    return 301;

The location directive modifiers

The location syntax is as follows:

location [ = | ~ | ~* | ^~ ] uri { ... }

You can use a modifier or not, here's what each of the modifiers do:

(no modifier)
If there's no modifier, the location is treated as prefix and matched from beginning of the string.
The equals sign means that the string is matched exactly.
The tilde initiates a case insensitive Regex match in the string.
Tilde followed by asterisk initiates a case sensitive Regex match in the string.
The carat followed by a tilde means that no Regular Expressions are checked.

Modifier act kind of like flags in JavaScript regex.

Below is a more detailed look into each one of these.

Location without a modifier

Just a string without any modifiers:

location / {
  # Will match anything that starts with forward slash, that's literally every
  # single page

location /foo {
  # Will match /foo and /foooooo and /fooxxxx and so on... The matching starts
  # from the beginning of the string, So it doesn't match /bar/foo.

The match is case sensitive, the above example won't match /FOO.

Literal strings

Use the equal sign = to tell Nginx that this is a literal match:

location = / {
  # Will match forward slash "/" and nothing more

location = /foo {
  # Will match /foo and nothing more

Case sensitive and case insensitive Regex match

This is where it gets interesting.

ℹ️ Nginx uses the PCRE engine to parse the Regular Expressions and the following characters needs to be escaped: .^$*+?()[{\|. And inside in character classes: ^-]\.

Tilde ~ enables Regex on the following string. So whenever you want to use Regular Expressions to match the path, use the tilde or the case insensitive tilde followed by asterisk ~*:

Here's some basic example of redirecting to a new path structure:

location ~ ^/tag/(.+) {
  return 301 /tags/$1;

Here's what's happening in it:

Start a case insensitive Regex.
Start matching from the beginning of the string.
Just the part of the URL you want to target.
A capturing group that "captures" rest of the URL to be used later.
The contents of the capturing group.

But see, it doesn't keep the query parameter around:

$ curl -I -s '' | grep Location:

This might be bad for many reasons (if you're doing tracking or "campaigns" or if it's just a part of your URL structure). But it can be added using the built-in $query_string variable:

location ~ ^/tag/(.+) {
  if ($query_string) {
    return 301 /tags/$1?$query_string;

  return 301 /tags/$1;

Nginx has a ton of built-in variables that can be used.

More location Regex examples

Set far expiring headers

Very common thing to do is to set far expiring headers for static file types, so they'll stay cached longer in your browser:

# Set expires to year on static file types
location ~* ^.+\.(css|js|jpg|jpeg|gif|webp|png|ico|gz|svg|svgz|ttf|otf|woff|woff2|eot|mp4|ogg|ogv|webm|mp4)$ {
  expires 365d;
  access_log off;

ℹ️ The literal mention of the dot needs to be escaped.

Prevent logging of favicons

Favicons often make logs unnecessarily loud and usually they don't serve much purpose. In this topsy-turvy proprietary technology world that we live in, where every provider wants to have their own favicon format, the matching isn't so straightforward. But there's almost nothing a good old regex pattern can't do:

location ~ (?:apple-touch-.*|favicon.*)\.(?:png|ico) {
  log_not_found off;
  access_log off;

You get the idea what location can do for you.

Testing redirects with curl

I should add that, never test redirects in a browser, because the browser "lies", they cache redirects heavily so you can never be sure what's going on. Better use curl for that:

$ curl -I ''

Using the above mentioned server block, that will give you the following output:

HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Sun, 28 Oct 2018 05:26:05 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive

You can grep that to make it more readable if you want:

$ curl -I -s '' | grep Location:

The -I option tells curl to fetch only the headers. The lower case -s is for silent, otherwise it shows the progress of the request.

This is the end of the post

Nginx also has the rewrite directive but that's for a another unforeseeable post.

Please do drop a comment below if you have something to add.

Comments would go here, but the commenting system isn’t ready yet, sorry.

  • © 2022 Antti Hiljá
  • About
  • All rights reserved yadda yadda.
  • I can put just about anything here, no one reads the footer anyways.
  • I love u!