2

I have the following in my nginx.conf:

location ~* /collections.*?products/([^/]+)/?$ {
    rewrite ^/collections.*?products/([^/]+)/?$ /$1.html;
    rewrite ^([^_]*)_([^_]*)_(.*)$ $1-$2-$3;
    rewrite ^([^_]*)_(.*)$ $1-$2 permanent; 
}  

To rewrite requests such as

"/collections/products/someproduct/" to "/someproduct.html"
"/collections/products/some_product/" to "/some-product.html"
"/collections/products/some_other_product/" to "/some-other-product.html"

However I can only get a 301 redirect to occur if the last rewrite directive (containing the permanent flag) matches and processes, e.g. my 2nd example. In the other 2 instances I get a 302 temporary redirect. How can I process these multiple rewrite directives in this location block and return 301 redirect regardless which ones match? If I put a permanent flag on all rewrite directives, it will stop processing after first match.

Nick Rolando
  • 123
  • 2
  • 7
  • I tend to have one rewrite per location block. Not sure why happens when you put three in. What problem are you trying to solve, or what are you trying to achieve with three rewrites in one block? Your question isn't clear. – Tim Dec 14 '17 at 07:14

2 Answers2

1

You can convert _ to - recursively and independently of the rewrite...permanent.

For example:

location ~* /collections.*?products/([^/]+)/?$ {
    rewrite ^(.*)_(.*)$ $1-$2 last;
    rewrite ^/collections.*?products/([^/]+)/?$ /$1.html permanent; 
}

The second rewrite is executed only after the first rewrite fails to find any more underscores. See this document for more.

Richard Smith
  • 11,859
  • 2
  • 18
  • 26
  • What does the `last` keyword do? From the docs, I understood it as it will stop processing current rewrites and start looking for new `location` directive.. As there's no other `location` directive for this URI, it didn't seem like the right choice. – Nick Rolando Dec 17 '17 at 18:39
  • `nginx` searches for a matching `location` (even if there is only one), `last` in `location` context causes that process to be restarted with the new URI. – Richard Smith Dec 17 '17 at 20:32
0

You can treat 302 status code as an “exception”, and “catch” it with http://nginx.org/r/error_page.

location ~* /collections.*?products/([^/]+)/?$ {
    rewrite ^/collections.*?products/([^/]+)/?$ /$1.html;
    rewrite ^([^_]*)_([^_]*)_(.*)$ $1-$2-$3;
    rewrite ^([^_]*)_(.*)$ $1-$2 permanent;
    error_page 302 =301 @302to301;
}
location @302to301 {
    return 300; # 300 is just a filler here, error_page dictates status code
    #return 301 $sent_http_location;
}

The technique is similar to my 301-302-redirect-w-no-http-body-text.nginx.conf, as per a related question on producing 301/302 redirects without HTTP Response Body.

Note that within @302to301, you have a choice between two return statements above; however, the return code is irrelevant in the context of this handler, as the error_page directive above ensures that all 302 codes get changed to 301 regardless of what the subsequent code is.

In other words, the only difference between the two return statements above will be the content of the HTTP Response Body, which no browser ever shows for 301 responses anyways, so, you might as well go with the shorter body-less return 300 version.

cnst
  • 12,948
  • 7
  • 51
  • 75