How to Remove Extensions From, and Force the Trailing Slash at the End of URLs?

16,066

So I wrote a set of Rewrite rules that did what you wanted, but it completely broke my website. I realized that what you want is probably not what you need. Adding trailing slashes to the end of all URLs really messes with the semantics of the URL in that you're no longer accessing the file /foo but the content listing of the directory /foo/.

For example:

changing /mypage to /mypage/ will probably break any relative links. If you reference a Javascript file <script src="myscript.js">, instead of looking for /myscript.js, the browser will look for /mypage/myscript.js. You would need to change your source to read <script src="../myscript.js"> which 1) doesn't make sense to the author, and 2) looks uglier than not having trailing slashes.

For reference:

RewriteCond %{REQUEST_FILE}\.html -f
RewriteRule (.*)$ $1.html [L]

RewriteCond %{REQUEST_FILE}\.php -f
RewriteRule (.*)$ $1.php [L]

RewriteCond %{REQUEST_FILE}\.cgi -f
RewriteRule (.*)$ $1.cgi [L]

would change only php, cgi, and html extensions, but a better idea would be to use Apache2 content negotiation (with MultiViews).

Edit:

The original code. Or at least part of it. I broke it, and then cut it down to the above, and now I can't quite remember what I did. But it does everything except remove trailing extensions.

# This block adds the trailing slash
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond /your/web/directory%{REQUEST_URI}\.html -f [OR]
RewriteCond /your/web/directory%{REQUEST_URI}\.php -f [OR]
RewriteCond /your/web/directory%{REQUEST_URI}\.cgi -f
RewriteRule .* %{REQUEST_URI}/ [R=301,L]

# These blocks redirect /foo/ to /foo.html and so on
RewriteCond %{REQUEST_URI} (.*)/$
RewriteCond /your/web/directory%1\.html -f
RewriteRule (.*)/$ $1.html [L]

RewriteCond %{REQUEST_URI} (.*)/$
RewriteCond /your/web/directory%1\.php -f
RewriteRule (.*)/$ $1.php [L]

RewriteCond %{REQUEST_URI} (.*)/$
RewriteCond /your/web/directory%1\.cgi -f
RewriteRule (.*)/$ $1.cgi [L]

You can email me at mazin (at) aztekera.com if you'd like.

Share:
16,066
Admin
Author by

Admin

Updated on September 17, 2022

Comments

  • Admin
    Admin over 1 year

    Example of current file structure:

    example.com/foo.php  
    example.com/bar.html  
    example.com/directory/  
    example.com/directory/foo.php  
    example.com/directory/bar.html  
    example.com/cgi-bin/directory/foo.cgi*  
    

    I would like to remove HTML, PHP, and CGI extensions from, and then force the trailing slash at the end of URLs. So, it could look like this:

    example.com/foo/  
    example.com/bar/  
    example.com/directory/  
    example.com/directory/foo/  
    example.com/directory/bar/  
    example.com/cgi-bin/directory/foo/
    

    I am very frustrated because I've searched for 17 hours straight for solution and visited more than a few hundred pages on various blogs and forums. I'm not joking. So I think I've done my research.

    Here is the code that sits in my .htaccess file right now:

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

    As you can see, this code only removes .html (and I'm not very happy with it because I think it could be done a lot simpler). I can remove the extension from PHP files when I rename them to .html through .htaccess, but that's not what I want. I want to remove it straight. This is the first thing I don't know how to do.

    The second thing is actually very annoying. My .htaccess file with code above, adds .html/ to every string entered after example.com/directory/foo/. So if I enter example.com/directory/foo/bar (obviously /bar doesn't exist since foo is a file), instead of just displaying message that page is not found, it converts it to example.com/directory/foo/bar.html/, then searches for a file for a few seconds and then displays the not found message. This, of course, is bad behavior.

    So, once again, I need the code in .htaccess to do the following things:

    • Remove .html extension
    • Remove .php extension
    • Remove .cgi extension
    • Force the trailing slash at the end of URLs
    • Requests should behave correctly (no adding trailing slashes or extensions to strings if file or directory doesn't exist on server)
    • Code should be as simple as possible

    @Kronbernkzion excellent. The only issue I'm having now is 404's don't seem to work right and leads me to a real funky place, I can't even use an absolute 404 redirect.

    ErrorDocument 404 http://www.google.com
    

    Did you come across this? How did you get past it?

    Aside from the 404 rewrite, the full code I've used was:

    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_URI} (.*)/$
    RewriteCond %{REQUEST_FILENAME}\.html -f
    RewriteRule (.*)/$ $1.html [L]
    
    RewriteCond %{REQUEST_URI} (.*)/$
    RewriteCond %{REQUEST_FILENAME}\.php -f
    RewriteRule (.*)/$ $1.php [L]
    
    RewriteCond %{REQUEST_URI} (.*)/$
    RewriteCond %{REQUEST_FILENAME}\.cgi -f
    RewriteRule (.*)/$ $1.cgi [L]
    
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME}\.html -f [OR]
    RewriteCond %{REQUEST_FILENAME}\.php -f [OR]
    RewriteCond %{REQUEST_FILENAME}\.cgi -f
    RewriteRule .* %{REQUEST_FILENAME}/ [R=301,L]
    </IfModule>
    
    • Admin
      Admin about 14 years
      I feel your pain
    • Admin
      Admin about 14 years
      Question: Does these rewrite rules need to match ALL Extensions? For example, if you have /foo.html and /bar.php, are you going to have a rule that explicitly matches /foo/ to /foo.html and /bar/ to /bar.php? Or do you need a single generic entry that will match /foo/ to both /foo.html OR /foo.php (depending on which one exists)?
    • Admin
      Admin about 14 years
      I need a single generic entry. Nothing file specific, I just want to remove extensions from those three file types.
    • Admin
      Admin about 14 years
      @Kronbernkzion, I've updated my answer. I don't think you'll like what it says though.
    • Admin
      Admin about 12 years
      There's not real reason to use a trailing slash. Look at zendesk.com who uses wordpress, they have configured their permaliks to not use a trailing slash. I've also used the same setup on a few domains and Google crawls them just fine, and they look better IMHO than with a trailing slash. I'd upgrade to WP if you can and just setup 301's from your old static or dynamic pages to the new WP pages.
  • Sean
    Sean about 14 years
    I will not have duplicate filenames, so there will never be foo.html and foo.php, so Apache won't need to decide which file should be served.
  • Sean
    Sean about 14 years
    Response to your edit: Again, all I need it to do is remove every .html, .php and .cgi extension and then add a trailing slash afterwords. I have no doubt that this can be done. I know it can easily be done through PHP file, and I know a lot of people prefer this method, but I want to do it through .htaccess.
  • Mark Henderson
    Mark Henderson about 14 years
    Whether or not there are duplicate filenames or not is irrelevant. Apache will not know which one you even MEAN, let alone serve it. You will need a system with some intelligence that you can program your rules into, so that it knows which content to serve. That option is PHP or ASP. This is what we did when we were in a similar situation. We funneled EVERY request into a single .php file, which then acted as our proxy. The PHP file would then query each version of the URL until it found one that did not return a status 404, and it then served that content out to the browser.
  • Mark Henderson
    Mark Henderson about 14 years
    Sorry, just saw your comment - must have been written whilst I was writing mine. This cannot be done by .htaccess - it's simply a too complex task for its simple rewriting engine to do.
  • Sean
    Sean about 14 years
    So you're saying that it can only process one file type extension. It can only be .html or only .php, it can't do both? Are you sure about that?
  • Mark Henderson
    Mark Henderson about 14 years
    Ahhh, you know what? I think maybe Mazin's answer might be on track. Let me know how you go with his, he may have taken an angle that I hadn't thought of.
  • styks
    styks about 14 years
    I know exactly what I need. Esthetics and functionality of trailing slashes in URLs is topic for itself, so we won't go into that. No worry about breaking links because I am in process of building website from scratch.
  • styks
    styks about 14 years
    What would you add to the code above to force trailing slashes at the end of URLs?
  • styks
    styks about 14 years
    OK, I added most of what I had. I still think you're going down the wrong path by forcing trailing slashes given that it breaks the path semantics, but you seem pretty determined.
  • Admin
    Admin about 14 years
    As I already said above, I am not interested in doing it with PHP.