0

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?

2 Answers 2

2

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.

3
  • I got nginx: [emerg] "map" directive is not allowed here. Does map can not be used inside server block? I don't have access to modify nginx outside server block. Sep 14, 2021 at 3:37
  • Found these question stackoverflow.com/questions/27358804, so I think map is not an option for me. Is it possible to do just rewrite (regex for old query params) (new links)? I don't understand much about regex. Sep 14, 2021 at 3:45
  • That's a nice trick to concat the query params and store them as a variable. Sep 15, 2021 at 2:53
1

I ended up with this solution.

location / {
  if ($arg_p = contact) { return 301 /contact; }
  if ($args ~ p=static&id=career) { return 301 /career; }
  if ($arg_p = static) { return 301 /about; }
  if ($args ~ p=catalog&action=images&cat_id=1) { return 301 /product-category/category-a; }
  if ($args ~ p=catalog&action=images&cat_id=2) { return 301 /product-category/category-b; }
  # and other cat_id
  if ($args ~ p=catalog&action=viewimages&pid=1&cat_id=1) { return 301 /product/product-a; }
  if ($args ~ p=catalog&action=viewimages&pid=2&cat_id=1) { return 301 /product/product-b; }
  # and other pid
  if ($arg_p = catalog) { return 301 /products; } #other p=catalog defaults to /products
  try_files $uri $uri/ /index.php$is_args$args;
}

It's working, but it can't handle the case when the query params order is not written below, e.g /?id=career&p=static (id and p is switched around)

Also cat_id in p=catalog&action=viewimages is not used, but when I remove the cat_id from the rules, p=catalog&action=viewimages&pid=10 always redirects to p=catalog&action=viewimages&pid=1, so I had to put the cat_id.

If someone had better idea to handle dynamic order for the query params, feel free to post as an answer. I'll mark it as accepted if it works.

EDIT: For dynamic order query params and much more cleaner if, see Ivan Shatsky's answer

1
  • See the update to my answer. Sep 14, 2021 at 15:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.