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

How to Remove Extensions From, and Force the Trailing Slash at the End of URLs? - .htaccess files are extremely useful in many cases for users who either do not have root permissions or for users who simply aren't comfortable in making changes in their web server's configuration file. Trying to debug .htaccess not working isn't always the easiest thing to do, however, hopefully by checking the discuss below mentioned about mod-rewrite, htaccess, url-rewriting, , .htaccess common problems as well as the troubleshooting tips, you'll have a better grasp on what you may have to modify to get your .htaccess file running smoothly.Problem :


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>

Solution :

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.



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.



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.


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.

Additionally, if you would like to do some further testing, give the htaccess tester tool a try. It allows you to specify a certain URL as well as the rules you would like to include and then shows which rules were tested, which ones met the criteria, and which ones were executed.

Comments

Popular posts from this blog

Rewrite in Mediawiki, remove index.php, .htaccess

.htaccess rewrite wildcard folder paths from host

Using .htaccess to set a cookie and 301 redirect