May 10, 2020

NGINX redirect that matches query string via MAP directive

Alexey Doronin
Founder, developer, designer

Suppose we need to make NGINX redirect according to this scheme:

example.com/tags?tag=cool => example.com/something 

The original url with parameters must be redirected to the new url. The parameter name and value are not used in the new url.

A standard redirect for this type of issue will not work.

## It will not work

location = /tags?tag=cool {
  return 301 https://example.com/something;
}

The solution is to use the MAP directive.

MAP

Before all server declarations, at the beginning of the configuration file we write the “mapping” table:

map $arg_tag $tag_new_destination {
    'cool'    /something;
    '6.0.1'    /tags/601;
    'alfa'    /newone;
}

In this example, the redirects will be:
example.com/tags?tag=cool => example.com/something
example.com/tags?tag=6.0.1 => example.com/tags/601
example.com/tags?tag=alfa => example.com/newone

The $arg_tag — this is nginx variable, it is created by the pattern $arg_* from query string. Here we have a parameter ?tag, this is why the variable is named $arg_tag.

$tag_new_destination — our custom variable.

If the table is large, nginx asks you to increase the size of map_hash_bucket_size. This should be changed in the nginx.conf file. For example, for 100 addresses, with long Russian transliterations, a value of 256 was enough.

map_hash_bucket_size 256;

Location

In the appropriate server section you need to write:

location /tags {
    error_page 420 = @tags_redirects;
 
    if ( $args ~ "tag=" ) { return 420; }
    try_files $uri $uri/ /index.php?$query_string;
}
 
location @tags_redirects {
    if ($tag_new_destination) {
      return 301 $tag_new_destination;
    }
}

We declare the location we need, in this case /tags. We want redirects to be processed only at these addresses. It contains a condition for the presence of a string with the tag parameters ($args ~ "tag ="). If this condition does not work (that is, the address /tags is without parameters and a redirect is not needed), then we forward it to try_files (or another standard solution).

If the query condition with the parameter is triggered, then its location @tags_redirects is declared, where mapping is checked. $tag_new_destination is just a variable that is equal to 1 if it exists in the mapping table.

If there are many redirects, they can be moved to a separate file.

At the beginning of the configuration file, write include for the file, where there will be a list with redirects:

map $arg_tag $tag_new_destination {
  include includes/tags-redirects.conf;
}

The includes/tags-redirects.conf file lists all matches of old and new addresses. For our example, its content will be:

'cool'  /something;
'6.0.1' /tags/601;
'alfa'  /newone;