6

I want to redirect URLs without slash to the path with trailing slash. So /some-url to /some-url/

And the rest of the URLs, like

  • /some-url.xml
  • /some-url?
  • /some-url?q=v
  • /some-url/

Should stay without redirection.

I found this article https://www.ateamsystems.com/tech-blog/nginx-add-trailing-slash-with-301-redirect-without-if-statements/ in which author suggests to use following rule:

location ~ ^([^.\?]*[^/])$ {
   try_files $uri @addslash;
}

location @addslash {
    return 301 $uri/;
}

Unfortunately this doesn't really work. Because url /some-url?q=v gets redirected to /some-url/

Could you suggest how to change regular expression to make it work?

Pawel
  • 466
  • 1
  • 7
  • 20

3 Answers3

7

This should solve the problem:

location / {
    if ($request_uri ~ ^([^.\?]*[^/])$) {
        return 301 $1/;
    }

    try_files $uri $uri/ /index.php$is_args$args;
}
Pawel
  • 466
  • 1
  • 7
  • 20
1

The query string begins at the ? and is not part of the normalized URI used when matching location and rewrite directives. The entire URI is available as the $request_uri variable. You could use your regular expression within an if block:

if ($request_uri ~ ^([^.?]*[^/])$ ) { return 301 $1/; }

See this document for more, and this caution on the use of if.

Richard Smith
  • 45,711
  • 6
  • 82
  • 81
  • this is so weird, it doesn't work for me at all. Url like **showroom.dev/carpage?product_id=1234** still redirects to **showroom.dev/carpage/**. Feels like nginx is misinterpreting regex. – Pawel Oct 10 '17 at 13:57
  • Ok, seems like in your regex one backslash was missing, it should be **if ($request_uri ~ ^([^.\?]*[^/])$ ) { return 301 $1/; }** – Pawel Oct 10 '17 at 15:04
  • Escaping a `?` within a character class should not be necessary - I am not sure why you need to escape it on your platform. Also, your regular expression fails the second case in your question as a `?` in the last position matches the second character class. – Richard Smith Oct 10 '17 at 15:41
  • True. But actually I don't need the second case. Anyway, thank you Richard. – Pawel Oct 11 '17 at 06:22
1

I figured out how to do this without an if statement! This solves all the problems you mentioned (except case #2, and in case #3 it redirects but retains the query string).

# 301 try_file for trailing slash
location ~ ^([^.\?]*[^/])$ {
  try_files $uri @addslash;
}

# 301 redirect for trailing slash
location @addslash {
  return 301 $uri/$is_args$args;
}

# Root directory location handler
location / {
    try_files $uri/index.html $uri $uri/ /index.php?$query_string;
}
hisnameisjimmy
  • 1,508
  • 2
  • 17
  • 24