Hide Html Extension + Redirect .html Version + Special Case Exception

- 1 answer

I have a hard time understanding htaccess mod_rewrite and while I found several related questions + answers, I unfortunately can't get my very specific situation to work correctly (mod_rewrite is even after hours of searching a book of seven seals to me to be honest)

I have foo.html and bar.html within my root directory. Now, I'd like to have foo.html as the default directory index (solved, easy), but from there I do not get it.

What I want to achieve is:

  1. hiding the .html extensions
    user should be able to type /bar to get /bar.html without seeing the .html (for every .html)
  2. 301 redirecting .html version
    user should be able to type /bar.html and see /bar in the url (avoid duplicate, for every .html)
  3. The most tricky part:
    As foo.html is default directory index, typing / already shows (transparently) /foo.html, but I need typing /foo.html to resolve to / as well as typing /foo to resolve to /


Try putting these rules in the htaccess file in your document root:


# 1. hiding the .html extensions
RewriteCond %{REQUEST_URI} ^/(.*?)/?$
RewriteCond %{DOCUMENT_ROOT}/%1.html -f
RewriteRule ^ /%1.html [L]

# 2. 301 redirecting .html version
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^\ ]+)\.html
RewriteCond %1 !foo$
RewriteRule ^ /%1 [L,R=301]

# 3. typing /foo.html to resolve to / as well as typing /foo to resolve to / 
RewriteCond %{REQUEST_URI} ^/(.*?/?)foo(\.html)?$
RewriteRule ^ /%1 [L,R=301]

Also, make sure you have Multiviews turned off:

Options -Multiviews

The first rule has 2 conditions, the first groups the URI everything between the first / and a possible last /. The references it using %1 in the next condition which sees if the /path/to/www/document/root/%1.html is a file that exists. If both are true, it internally rewrites the URI to include a .html at the end.

The second rule has 2 conditions, the first matches against the actual request as opposed to the URI (which can change as the rules are being applied and rewrites happen). It sees if there's a request that ends with .html, and if so, the second condition makes sure that it isn't foo.html request (since the last rule handles that). If both are true, then it redirects the browser to the part of the URI without the html, again using %1 to reference the grouping in the match from the first condition ([^\ ]+).

The last rule checks if the request is for a foo or foo.html. If so, redirect removing that part of the URI.