Take the 2-minute tour ×
Webmasters Stack Exchange is a question and answer site for pro webmasters. It's 100% free, no registration required.

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

I would very much appreciate any help. And to first person that gives me the solution, I'll send two $50 iTunes Store gift cards for US store. If this offends anyone, I am truly sorry and I apologize.

Thanks in advance.


@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>
share|improve this question

migrated from serverfault.com Feb 18 '11 at 9:24

This question came from our site for professional system and network administrators.

    
I feel your pain –  Mark Henderson Mar 19 '10 at 1:24
    
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)? –  Mark Henderson Mar 19 '10 at 1:29
    
I need a single generic entry. Nothing file specific, I just want to remove extensions from those three file types. –  Kronbernkzion Mar 19 '10 at 1:33
    
@Kronbernkzion, I've updated my answer. I don't think you'll like what it says though. –  Mark Henderson Mar 19 '10 at 1:46
    
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. –  Anagio Feb 28 '12 at 9:31

5 Answers 5

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|improve this answer
    
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. –  Kronbernkzion Mar 19 '10 at 2:20
    
What would you add to the code above to force trailing slashes at the end of URLs? –  Kronbernkzion Mar 19 '10 at 2:23
    
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. –  erjiang Mar 19 '10 at 2:51

Sounds like you're trying to do something similar to a REST-compliant URL implementation.

I've seen this covered before on SO, here: http://stackoverflow.com/questions/395650/url-mapping-in-php

You may be able to adapt the 2 top solutions there to your needs.

share|improve this answer
    
As I already said above, I am not interested in doing it with PHP. –  Kronbernkzion Mar 19 '10 at 3:58

Mazin, thank you so much for your help and for showing me the right direction! The code below works for removing .html, .php and .cgi extensions as well as for forcing trailing slashes to the end of URLs. The final working code looks like this:

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]

I am extremely happy with the way this turned out.

I've sent one $50 iTunes gift card to Mazin as a big thanks for helping out.

share|improve this answer

If you're know the exact format of each URL that you need, then it's pretty straight forward. If you don't know which extension you need to match though, well, then I'm fairly sure that's impossible.

For example, if you KNOW that you have foo.html and bar.php - and that /foo/ needs to match foo.html and you KNOW that /bar/ needs to match /bar.php, then that can be done. But, if someone uploads /cat.php and someone enters /cat/ - the system will not know whether it needs to match /cat.html or /cat.php - you need to tell it. If you were just dealing with one extension, that wouldn't be so bad, you could just tack it onto every request.

To rewrite /foo/ to /foo.html then you would do:

RewriteRule (/foo/) /foo.html

Easy

If you want to match every request to a SINGLE extension, then you could do:

RewriteRule (/foo/)(.*)(/) /foo/$2.html

This will map /foo/mypage/ to /foo/mypage.html (and you can expand this to multi-level directory using some RegEx-fu that is above my pay grade).

Now, I don't actually know that that's what you're asking for, so please comment on my answer, or update your question, if these rules are not going to work for you.

-- Edit --

Just noticed your response to my comment above. There is no way you can do what you want to do with .htaccess, because .htaccess does not have the ability to check to see if a particular file requests exists. How will it know that /foo/ is meant to be /foo.html or /foo.php or /foo.cgi? Your only way around it will be to:

1) Funnel EVERY request through a single.php file that then has the ability to know/check to see if the .php or .html or .cgi version of the file exists, and then forwards your request that way

2) Create a .htaccess file that contains an entry for every single unique file

share|improve this answer
    
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. –  Kronbernkzion Mar 19 '10 at 1:49
    
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. –  Kronbernkzion Mar 19 '10 at 1:56
    
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 Mar 19 '10 at 1:58
    
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. –  Mark Henderson Mar 19 '10 at 1:59
    
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? –  Kronbernkzion Mar 19 '10 at 2:04

Found a one that worked for me, Imported my Blogger posts to my Wordpress, and then my back links didn't work, so I just replaced my .htaccess with this example, check it out!

http://www.high-on-it.co.za/2011/02/removing-trailing-html-from-url/

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.