.htaccess Add Trailing Slash to URL

If you are rewriting your URLs using .htaccess and mod_rewrite, you might run into a problem where trailing slashes are ignored.

Here is an example scenario. Say you have PHP script that takes query arguments like this (replace example.org with your domain, of course):

http://example.org/index.php?page=content&title=welcome

You are rewriting it to look like this:

http://example.org/content/welcome/

For reference, here’s an example .htaccess script that would do that (there are plenty of other ways, this is one basic example):

RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ /index.php?page=$1&title=$2 [QSA,L]

And here is the problem with that! If you try a URL like this (without the trailing slash):

http://example.org/content/welcome

404! OH NOES!!

So, one alternative is to change the .htaccess directive to look like this:

RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/?$ /index.php?page=$1&title=$2 [QSA,L]

So now both of these URLs work!

http://example.org/content/welcome/, AND
http://example.org/content/welcome

BUT WAIT! Trailing slashes are prettier, and we don’t want two separate URLs for the same content! Google doesn’t even like that! OOOH NOOOES!!

How to force a trailing slash in the URL:

  • Make your rewrites require the trailing slash, as with the first example:
    RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ /index.php?page=$1&title=$2 [QSA,L]
  • Add this code below your regular rewrites:
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
    RewriteRule (.*)$ http://example.org/$1/ [R=301,L]

Note: This should work no matter how your application handles URLs. Some applications, however, such as WordPress, pull in the entire URL query regardless of slashes. If your application inherently does not care about trailing slashes in its RewriteRules, then you must put these directives before your other rewrites. The reason for that is because if it goes after them, then the application’s own RewriteRule will go into effect, accepting the URL without the trailing slash, and will likely ignore any subsequent rules (because of the [L] flag). The reason I suggest in this tutorial to put the script after your normal rewrites is for optimization and that can only apply when your normal rewrites require a trailing slash. If the URL has a trailing slash, as is the desired scenario, then it should go ahead and apply the rules and not even bother looking at the redirection directive.

Another point to keep in mind is that this script will redirect and add a trailing slash even for bogus URLs. E.g. - if you type this hypothetical non-existent URL into the address bar:

http://example.org/garhtrehstreetreetree

It is going to rewrite and forward it to the following URL with a trailing slash:

http://example.org/garhtrehstreetreetree/

Before displaying the expected 404 error. Note that this is not typical behaviour of Apache server. However; I do not consider this quirk to be a big deal at all, and I believe that it is unavoidable since we are handling this with .htaccess.

Explanation of the directives and regular expressions:

The two RewriteCond lines test to make sure the requested filename is not an actual file or directory, respectively. This makes sense because we’re rewriting URLs here; the requested URLs are not actual files, they are references to actual files and query arguments.

The regular expression in the third RewriteCond does two things. It checks to make sure the requested “filename” does not have an extension. If for whatever reason it has an extension, then we probably don’t want to add a trailing slash! Note: This expression considers an extension anything that has a period followed by between 1 and 5 letters and numbers. That covers your .js and your .phtml, at least. Feel free to change the 1 and 5 to suit your application. The other thing the third RewriteCond does is make sure that the URL doesn’t already have a trailing slash. If it didn’t do that check, the redirect could attempt to loop forever and that’s annoying!

The last line, the RewriteRule, will capture the entire URL path. It then does a 301 redirect to the same URL path with a trailing slash appended.

IMPORTANT UPDATE (IMPORTANT UPDATE TO IMPORTANT UPDATE: IMPORTANT UPDATE NOW IRRELEVANT — SEE EVEN MORE IMPORTANT UPDATE): It appears that some configurations do not support lookaheads in regular expressions! I have a strong suspicion but do not know for sure that the issue is the Apache version. This tutorial should work fine for Apache 2, and may not work for Apache 1.3. That, specifically, was the case with the configurations I tested. Here is an alternative route if you are getting internal server errors with the original method:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
RewriteRule (.*)([^/])$ http://example.org/$1$2/ [R=301,L]

I do not think it is as pretty, but ya gotta do what ya gotta do!

EVEN MORE IMPORTANT UPDATE: After browsing the web for a minute, I came across another site that has a similar (but different) tutorial. After looking at the example therein, I metaphorically smacked my forehead and realized that a very small part of the original example (specifically, the lookahead ((?!/)) part) was not necessary, and it now works on all tested servers. The tutorial has been updated to reflect this change and is now even prettier than it was before. Thank you other person, I feel dumb now and hats off to you.

12 Responses to “.htaccess Add Trailing Slash to URL”

  1. Paul Montwill Says:

    Hi,
    I used your code to solve trailing slash problem but then my function to change http://www. to non-www didn’t work. Can you help, please? (switchstories.com).

    Regards

  2. Dan Says:

    Paul, could you paste the applicable code in here and we could have a look at it? I’m curious as well about what’s going on there…

  3. Paul Montwill Says:

    Sure, Dan. My site is http://switchstories.com and the code I use is:

    Options +FollowSymLinks
    RewriteEngine On
    RewriteBase /
    RewriteCond %{HTTP_HOST} ^www.switchstories.com [NC]
    RewriteRule ^(.*)$ http://switchstories.com/$1 [L,R=301]

    and let’s take news section: RewriteRule ^news\/$ ?action=news_List [L]

    When I installed your function http://switchstories.com/news was 301 redirected to the version with slash. But then http://www. was not redirected to non-www as I have it now.

    Thanks for support.

  4. Dan Says:

    Not sure if this has been tried already, but the non-www rules might have to come before the trailing slash code here (seems like it ought to catch it either way though)… Maybe it will be as simple as that? If that doesn’t work we can try another route…

  5. Paul Montwill Says:

    Doesn’t work :(

  6. Dan Says:

    I’ll e-mail you directly about it and see if we can find a solution…

  7. Paul Montwill Says:

    Dan, your solution solved the problem:
    Options +FollowSymLinks
    RewriteEngine On
    RewriteBase /

    RewriteCond %{HTTP_HOST} ^www.switchstories.com [NC]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$

    RewriteRule (.*)$ http://switchstories.com/1/ [R=301,L]

    RewriteCond %{HTTP_HOST} ^www.switchstories.com [NC]
    RewriteRule (.*)$ http://switchstories.com/1 [R=301,L]

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
    RewriteRule (.*)$ http://switchstories.com/1/ [R=301,L]

    THANKS!

  8. Dan Says:

    Sweet! Good to know it worked!

  9. Sraboni Says:

    how do I redirect
    http://www.somesite.com/elle to
    http://www.somesite.com/example.php

  10. Dan Says:

    Sraboni,
    This post is just about adding a trailing slash to URLs, but to point you in the right direction, you might try here, here, or otherwise try Google

  11. Sraboni Says:

    Thanks Dan. I have done this,
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !(/[a-zA-Z0-9]{1,5})\.html$
    RewriteRule ^(.*)$ http://www.somesite.com/landing_page.php [L]

    Its showing http://www.somesite.com/landing_page.php. its fine. But I want to show the url as
    http://www.somesite.com/abc

  12. Dan Says:

    You might try

    RewriteEngine On
    RewriteBase /
    RewriteRule ^abc/?$ landing_page.php [QSA,L]

Leave a Reply