Nginx Redirect – How to Redirect Based on Query String in Nginx

nginx

I'm migrating old website links to new website, it will use same domain

Here is some old links.

Old Links New links
http://example.com/?p=contact /contact
http://example.com/?p=static&id=career /career
http://example.com/?p=static&id=about /about
http://example.com/?p=catalog&action=images&cat_id=1 /product-category/category-slug-1
http://example.com/?p=catalog&action=images&cat_id=2 /product-category/category-slug-2
http://example.com/?p=catalog&action=viewimages&pid=1&cat_id=1 /product/product-slug-1
http://example.com/?p=catalog&action=viewimages&pid=2&cat_id=3 /product/product-slug-2

The new product page doesn't have id in the URL, so I list all of them manually, and they are just 5 categories and 20 product pages in total.

This is what I've tried before I knew that nested if is not supported.

location / {
    if ($arg_p = contact) { return 301 /contact; }
    if ($arg_p = static) { 
        if ($arg_id = career) { return 301 /career; }
        # other static pages redirect to /about
        return 301 /about;
    }

    if ($arg_p = catalog) {
        if ($arg_action = images) {
            if ($arg_cat_id = 1) { return 301 /product-category/category-slug-1; }
            if ($arg_cat_id = 2) { return 301 /product-category/category-slug-2; }
            # other unlisted categories should redirect to /product-categories
            return 301 /product-categories;
        }
        if ($arg_action = viewimages) {
            if ($arg_pid = 1) { return 301 /product/product-slug-1/; }
            if ($arg_pid = 2) { return 301 /product/product-slug-2/; }
        }
        # other unlisted links defaults to /products
        return 301 /products;
    }
}

What is the config should be?

Best Answer

You can do it with several chained map blocks. Here is an idea:

map $arg_p $url_p {
    contact    /contact;
    static     $url_id;
    catalog    $url_action;
    # default value will be an empty string
}

map $arg_id $url_id {
    career     /career;
    about      /about;
    # other static pages redirect to /about
    default    /about;
}

map $arg_action $url_action {
    images     $url_cat_id;
    viewimages $url_pid;
    # other unlisted actions defaults to /products
    default    /products;
}

map $arg_cat_id $url_cat_id {
    1          /product-category/category-slug-1;
    2          /product-category/category-slug-2;
    # other unlisted categories should redirect to /product-categories
    default    /product-categories;
}

map $arg_pid $url_pid {
    1          /product/product-slug-1;
    2          /product/product-slug-2;
    # other unlisted products defaults to /products
    default    /products;
}

server {
    listen ...
    server_name ...
    ...
    if ($url_p) { # if '$url_p' variable is not an empty string
        return 301 $url_p;
    }
    location / {
        ...
    }
    ...
}

Some map blocks can be shortened, for example, lets assume you have 3 static pages /career, /clients and "default" page /about, 5 categories and 45 products:

map $arg_id $url_id {
    ~^(career|clients)$        /$1;
    default                    /about;
}

map $arg_cat_id $url_cat_id {
    ~^([1-5])$                 /product-category/category-slug-$1;
    default                    /product-categories;
}

map $arg_pid $url_pid {
    ~^([1-9]|[1-3]\d|4[0-5])$  /product/product-slug-$1;
    default                    /products;
}

Update

OP states he can't use map directive since he doesn't have an access to full nginx config but only to server block contents. While previous solution is far more elegant (and should be more effective in terms of performance), it is possible to do the same using only if blocks:

if ($arg_p = contact) { return 301 /contact; }

if ($arg_p = static) { set $page static_$arg_id; }
if ($page = static_career) { return 301 /career; }
if ($page) { return 301 /about; } # anything that is not 'career' redirected to '/about'

if ($arg_p = catalog) { set $action $arg_action; }

if ($action = images) { set $page category_$arg_cat_id; }
if ($page = category_1) { return 301 /product-category/category-a; }
if ($page = category_2) { return 301 /product-category/category-b; }
# ... other categories
if ($action = images) { return 301 /product-categories; } # unlisted category specified

if ($action = viewimages) { set $page product_$arg_pid; }
if ($page = product_1) { return 301 /product/product-a; }
if ($page = product_2) { return 301 /product/product-b; }
# ... other products
if ($action = viewimages) { return 301 /products; } # unlisted product specified

# if you want to process any unlisted action in some special way
# if ($action) { ... } # 'action' query argument neither 'images' nor 'viewimages'

This fragment can be placed either in server or location context.