Add slash to the end of every url (need rewrite rule for nginx)
Asked Answered
H

12

71

I try to get an / to every urls end:

example.com/art

should

example.com/art/

I use nginx as webserver.

I need the rewrite rule for this..

For better understanding check this:

http://3much.schnickschnack.info/art/projekte

If u press on a small thumbnail under the big picture it reloads and shows this url:

http://3much.schnickschnack.info/art/projekte/#0

If i now have a slash on all urls (on the end) it would work without a reload of the site.

Right now i have this settings in nginx-http.conf:

server {
  listen *:80;
  server_name 3much.schnickschnack.info;
  access_log /data/plone/deamon/var/log/main-plone-access.log;
  rewrite ^/(.*)$ /VirtualHostBase/http/3much.schnickschnack.info:80/2much/VirtualHostRoot/$1 last;
  location / {
    proxy_pass http://cache;
  }
}

How do I configure nginx to add a slash? (I think i should a rewrite rule?)

Hamilton answered 14/3, 2009 at 12:33 Comment(5)
Um... why would you want a / at th eend of all your URLs?Irresolution
I need it for a js gallery. If there is no "/" it reloads the site...Hamilton
I need the rewrite rule for nginx....Hamilton
So the question is "How do I configure nginx to add a slash?" Or is it "How do I configure nginx to work without a slash?"Dwanadwane
Question should: How do I configure nginx to add a slash?Hamilton
A
143

More likely I think you would want something like this:

rewrite ^([^.]*[^/])$ $1/ permanent;

The Regular Expression translates to: "rewrite all URIs without any '.' in them that don't end with a '/' to the URI + '/'" Or simply: "If the URI doesn't have a period and does not end with a slash, add a slash to the end"

The reason for only rewriting URI's without dots in them makes it so any file with a file extension doesn't get rewritten. For example your images, css, javascript, etc and prevent possible redirect loops if using some php framework that does its own rewrites also

Another common rewrite to accompany this would be:

rewrite ^([^.]*)$ /index.php;

This very simply rewrites all URI's that don't have periods in them to your index.php (or whatever file you would execute your controller from).

Alisun answered 12/10, 2010 at 8:11 Comment(9)
This is the only post on the interwebs that doesn't add a trailing slash to EVERYTHING. thanks for posting.Scoot
this one works much more reliably particularly with php frameworks that do there own pretty link rewritingRegardant
I have static html files so this: domain.com/about gets the file domain.com/about.html but still shows the URL without ".html". I am looking to add a slash after I hit the file but get a 404 now if I add a slash. So I want to remove the trailing slash (if exists) and with or without trailing slash gets "-html" file but shows "/" after. This does not work: rewrite ^/([a-zA-Z0-9]+)$ /$1.html; rewrite ^([^.]*[^/])$ $1/ permanent; Also for click on "about" in a menu severa times I get this domain.com/about/about/about etc..Miun
What about query strings?Checkers
@EtienneMartin It looks like it will rewrite domain.com/page?thing=abc into domain.com/page?thing=abc/. Or does nginx detect and handle query strings separately?Checkers
@Checkers Yes I thought the same when I saw it but it tested it and it looks like query strings are handled separately.Hymeneal
I could be doing something wrong here, but unfortunately this seems to break down when working with nested directories. I don't have nginx so I'm just testing it against regex101.com so removing the beginning of line condition to operate on the full URL http://example.com/foo/bar, the pattern [^.]*[^/]$ matches com/foo/bar. \/\w+[^.][^/]$ on the other hand, matches /bar but not /bar.html or /bar/Subalternate
Unfortunately, this doesn't seem to work if the URL contains a port: localhost:8080/something turns into localhost/something :TSomber
For those getting the port added to the URL, try with port_in_redirect off; in your config file, it worked for me.Cassiecassil
U
35
rewrite ^([^.\?]*[^/])$ $1/ permanent;

to avoid querystrings of a rest url getting a / tagged on.

e.g.

/myrest/do?d=12345

Uphroe answered 19/5, 2013 at 23:1 Comment(3)
how could you make the result ``` /myrest/do/?d=12345 ```Preclinical
@PatrickLeeScott Adding '?' to regex to exclude would be sufficient I thinkCrean
@PatrickLeeScott I'd added an answer that caseRoot
C
7

For nginx:

rewrite ^(.*[^/])$ $1/ permanent;
Contaminant answered 25/9, 2010 at 1:26 Comment(1)
This will add a slash to EVERYTHING - so you'll end up with index.php -> index.php/, and page?a=b -> page?a=b/ where what the OP surely wants is page?a=b -> page/?a=bCheckers
C
6

Odd that this is the first result in Google, but doesn't have a satisfactory answer. There are two good ways to do this I know of. The first is to straight-up check if the request will hit a file and only apply a rewrite condition if not. E.g.

server {
   # ...
   if (!-f $request_filename) {
     rewrite [^/]$ $uri/ permanent;
   }
   location / {
      # CMS logic, e.g. try_files $uri $uri /index.php$request_uri;
   }
   # ...
}

The second, which many prefer as they'd rather avoid any use of if that isn't 100% necessary, is to use try_files to send the request to a named location block when it won't hit a file. E.g.

server {
   # ...
   location / {
      try_files $uri $uri/ @cms;
   }
   location @cms {
      rewrite [^/]$ $uri/ permanent;
      # CMS logic, e.g. rewrite ^ /index.php$request_uri;
   }
   # ...
}
Cassondracassoulet answered 12/12, 2016 at 18:5 Comment(0)
L
3

it's too late but I want to share my solution, I've met issue with trailing slash and nginx.

#case : 
# 1. abc.com/xyz  => abc.com/xyz/
# 2. abc.com/xyz/ => abc.com/xyz/
# 3. abc.com/xyz?123&how=towork => abc.com/xyz/?123&how=towork
# 4. abc.com/xyz/?123&ho=towork => abc.com/xyz/?123&how=towork

and this is my solution

server { 
    ....
    # check if request isn't static file
    if ($request_filename !~* .(gif|html|jpe?g|png|json|ico|js|css|flv|swf|pdf|xml)$ ) {
       rewrite (^[^?]+[^/?])([^/]*)$ $1/$2 permanent;
    }
    ....
    location / {
    ....
    }
}
Lull answered 1/2, 2016 at 7:30 Comment(1)
I really like your solution. It helped me :). It can be also imporved with a more gneric regex for file matching: if ($request_filename !~* .[a-zA-Z0-9]+$ ) This should match any file.Anastice
B
2
server {
    # ... omissis ...

    # put this before your locations
    rewrite ^(/.*[^/])$ $1/ permanent;

    # ... omissis ...
}

If you want some kind of requests (say other than GET ones) to be prevented from doing this (usually it's about POST requests, as rewrite turns any request method into GET, which may break some of your site's dynamic functionality), add an if clause:

server {
    # ... omissis ...

    # put this before your locations
    if ($request_method = "GET" ) {
        rewrite ^(/.*[^/])$ $1/ permanent;
    }

    # ... omissis ...
}

You can also put the rewrite in a location block (if too), to make it more specific.

Burget answered 24/7, 2015 at 8:59 Comment(0)
L
1

using the rewrites from anthonysomerset in a Wordpress, I experimented problems accesing to /wp-admin dashboard due to reirection loop. But i solve this problem using the above conditional:

if ($request_uri !~ "^/wp-admin")
{
rewrite ^([^.]*[^/])$ $1/ permanent;
rewrite ^([^.]*)$ /index.php;
}
Looselimbed answered 12/3, 2014 at 1:27 Comment(0)
C
0

If nginx behind proxy with https, this snippet do correct redirect for $scheme

map $http_x_forwarded_proto $upstream_scheme {
    "https" "https";
    default "http";
}

server {
    ...
    location / {
        rewrite ^([^.\?]*[^/])$ $upstream_scheme://$http_host$1/ permanent;
    }
    ...
}

And on the upstream proxy pass the X-Forwarded-Proto header like:

location / {
    proxy_set_header X-Forwarded-Proto $scheme;
    ...
}
Cushman answered 16/3, 2018 at 6:29 Comment(0)
R
0

This rule solves query string case too:

location ~ ^/([^.]*[^/])$ {
  if ($query_string) {
    return 301 $scheme://$host/$1/?$query_string;
  }
  return 301 $scheme://$host/$1/;
}

The regex has taken from @marc's answer: rewrite ^([^.\?]*[^/])$ $1/ permanent;

The extra slash ^/ in regex is added to improve readability

Root answered 11/1, 2022 at 19:17 Comment(0)
M
0
location ~ ^([^.\?]*[^/])$ {
    rewrite ^([^.]*[^/])$ $1/ permanent;
}

just add this

Mauve answered 1/8, 2023 at 17:50 Comment(0)
R
0

A less cpu intensive regex [^/]$ that does the same job

# usage in rewrite 
rewrite [^/]$ $uri/ permanent;
# usage in location match and handling query string
location ~ [^/]$ {
    if ($query_string) {
      return 301 https://snapp.ir$uri/?$query_string;
     }
    return 301 https://snapp.ir$uri/;
}

rewrite regex runs before location matching and on our scale ( ~ 3 rps ) it demand an unusual amount of resources for an nginx pod; thus we use regex location which way more efficient in terms of cpu.

Root answered 28/5, 2024 at 19:33 Comment(0)
P
-4

Try this: ^(.*)$ http://domain.com/$1/ [L,R=301]

This redirects (Status code 301) everything ($1) without a "/" to "$1/"

Parterre answered 14/3, 2009 at 12:48 Comment(3)
Gomez: You should absolutely 301 (redirect) the non-slashed URL to the slashed one, or search-engines and such will see example.com/blah and example.com/blah/ as two completely separate pagesExtender
I just tested this. ^(.*)$ redirects everything regardless. I had to use ^(.*)[^/] instead, so it only redirects if there's no ending slash.Totality
This answer appears to provide syntax for Apache, but the original question is for nginx.Contaminant

© 2022 - 2025 — McMap. All rights reserved.