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.

1
  • 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, 2017 at 7:14

2 Answers 2

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.

2
  • 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. Dec 17, 2017 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. Dec 17, 2017 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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .