Htaccess: add/remove trailing slash from URL

Solution 1:

Right below the RewriteEngine On line, add:

RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R] # <- for test, for prod use [L,R=301]

to enforce a no-trailing-slash policy.

To enforce a trailing-slash policy:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*[^/])$ /$1/ [L,R] # <- for test, for prod use [L,R=301]

EDIT: commented the R=301 parts because, as explained in a comment:

Be careful with that R=301! Having it there makes many browsers cache the .htaccess-file indefinitely: It somehow becomes irreversible if you can't clear the browser-cache on all machines that opened it. When testing, better go with simple R or R=302

After you've completed your tests, you can use R=301.

Solution 2:

To complement Jon Lin's answer, here is a no-trailing-slash technique that also works if the website is located in a directory (like example.org/blog/):

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [R=301,L]


For the sake of completeness, here is an alternative emphasizing that REQUEST_URI starts with a slash (at least in .htaccess files):

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} /(.*)/$
RewriteRule ^ /%1 [R=301,L] <-- added slash here too, don't forget it

Just don't use %{REQUEST_URI} (.*)/$. Because in the root directory REQUEST_URI equals /, the leading slash, and it would be misinterpreted as a trailing slash.


If you are interested in more reading:

  • PR 3145 for Laravel
  • A discussion on commit 343c31e

(update: this technique is now implemented in Laravel 5.5)

Solution 3:

This is what I've used for my latest app.

# redirect the main page to landing
##RedirectMatch 302 ^/$ /landing

# remove php ext from url
# https://stackoverflow.com/questions/4026021/remove-php-extension-with-htaccess
RewriteEngine on 

# File exists but has a trailing slash
# https://stackoverflow.com/questions/21417263/htaccess-add-remove-trailing-slash-from-url
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/?(.*)/+$ /$1 [R=302,L,QSA]

# ok. It will still find the file but relative assets won't load
# e.g. page: /landing/  -> assets/js/main.js/main
# that's we have the rules above.
RewriteCond %{REQUEST_FILENAME} !\.php
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f 
RewriteRule ^/?(.*?)/?$ $1.php