How to configure redirects to url with trailing slash in nginx?
Asked Answered
A

3

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?

Amos answered 10/10, 2017 at 11:53 Comment(0)
A
7

This should solve the problem:

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

    try_files $uri $uri/ /index.php$is_args$args;
}
Amos answered 10/10, 2017 at 15:7 Comment(0)
K
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.

Kropotkin answered 10/10, 2017 at 12:36 Comment(4)
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.Amos
Ok, seems like in your regex one backslash was missing, it should be if ($request_uri ~ ^([^.\?]*[^/])$ ) { return 301 $1/; }Amos
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.Kropotkin
True. But actually I don't need the second case. Anyway, thank you Richard.Amos
R
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;
}
Razo answered 15/2, 2018 at 23:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.