7

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>
5
  • I feel your pain Mar 19, 2010 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)? Mar 19, 2010 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, 2010 at 1:33
  • @Kronbernkzion, I've updated my answer. I don't think you'll like what it says though. Mar 19, 2010 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, 2012 at 9:31

4 Answers 4

6

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.

3
  • 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, 2010 at 2:20
  • What would you add to the code above to force trailing slashes at the end of URLs?
    – Kronbernkzion
    Mar 19, 2010 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, 2010 at 2:51
3

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.

1

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

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

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

1
  • As I already said above, I am not interested in doing it with PHP.
    – Kronbernkzion
    Mar 19, 2010 at 3:58
0

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

6
  • 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, 2010 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, 2010 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. Mar 19, 2010 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. Mar 19, 2010 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, 2010 at 2:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.