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:
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.