12

I need a rule that will add an .html extension whenever there is 'not' a trailing slash.

A new client recently changed ecommerce scripts and the new version handles SEO differently and changed all of their 16,000+ product links. This was not caught prior to the site being re-indexed so we need to redirect the old to the new..

All products used to have links like this domain.com/category/productname but are now domain.com/category/productname.html

Category links did not change and all are like this domain.com/category/ (with trailing slash)

Scott
  • 121
  • 1
  • 1
  • 3

10 Answers10

17

The answer from @david-wolever redirects everything that does not end in .html (or the root) to the same URL with an added .html extension, meaning it appends a .html extension to things like CSS and JavaScripts files, e.g. it will redirect /style.css to /style.css.html which is not likely what you want. It also has the spaces after ! character which will likely caused @greggles 500s

This redirects URLs which do not end in a dot followed by 3 or 4 alphanumeric characters:

RewriteEngine On
RewriteCond %{REQUEST_URI} !\.[a-zA-Z0-9]{3,4}
RewriteCond %{REQUEST_URI} !/$
RewriteRule ^(.*)$ $1.html

Or for finer grain control, whitelist the extensions you do not want .html appended to e.g.

RewriteCond %{REQUEST_URI} !\.(html|css|js|less|jpg|png|gif)$
Paul Groves
  • 3,983
  • 2
  • 23
  • 24
  • 1
    Can I escape only for one url? because if I use this on wordpress site, I will not be able to go to admin page. cuz when i type "domain/wp-admin" it will redirect me to domain/wp-admin.html and return 404. Same thing happens with all the answers on this thread. – Tharaka Nuwan May 03 '16 at 10:34
  • 1
    Unfortunately this does not work on urls with hash fragments, which is limits applicability of this – Andrew Savinykh Jan 18 '17 at 09:50
  • I think you need to add $ at the end of the first approach and also there are extensions like .js or .woff2 that are out of that bounds. I would edit with something like RewriteCond %{REQUEST_URI} !\.[a-zA-Z0-9]{2,5}$ – Carlos Verdes Jan 11 '23 at 11:05
10

This option is similar to @remco's, but doesn't require the use of [R] (an "external" redirect sent back to the brower). I also added a missing \ in the first condition:

Options FollowSymLinks

RewriteEngine On
RewriteCond %{REQUEST_URI} !^.*\.html$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ %{REQUEST_FILENAME}.html
Thomas Leonard
  • 7,068
  • 2
  • 36
  • 40
4

This is old thread but it is still worth posting a working/tested answer for this problem:

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^.]+?)/?$ /$1.html [L,R=302]

Skipped RewriteCond %{REQUEST_FILENAME} !-f condition since regex pattern is not matching any URI with dot in it.

anubhava
  • 761,203
  • 64
  • 569
  • 643
  • My site currently allows example.com/test and example.com/test.html to work. I want to forward all non html to force .html. Can I use your above, and change 302 to 301? – user1040259 Aug 23 '16 at 04:19
  • thanks! You helped in the past, I need to reverse the settings. Thank you. http://stackoverflow.com/questions/39092679/htaccess-301-redirect-to-html – user1040259 Aug 23 '16 at 06:42
4
RewriteEngine On
RewriteCond %{REQUEST_URI} ! \.html$
RewriteCond %{REQUEST_URI} ! /$
RewriteRule ^(.*)$ $1.html

You might want to throw an [R] in there, or something too. See docs: http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriterule (search for "redirect|R").

David Wolever
  • 148,955
  • 89
  • 346
  • 502
  • So, I've tried this (including adding a [R=301,L] on the end) and I get a 500 error for any requests. I tried a simple RewriteRule on the server and it worked, so I assume there is a flaw in this and not in the general configuration of .htaccess/mod_rewrite on that server. – greggles Aug 29 '12 at 17:45
3

The answer from @paul.grov.es seemed to work fine for me, but then I noticed that my javascript wasn't working anymore.

The solution is obvious, and mentioned by him: a '.html' was being added to the url, given that the javascript extension's only have 2 chars.

So, my solution turned out to be:

RewriteEngine On
RewriteCond %{REQUEST_URI} !\.[a-zA-Z0-9]{2,4}
RewriteCond %{REQUEST_URI} !/$
RewriteRule ^(.*)$ $1.html
bmareal
  • 31
  • 2
  • This is actually the right answer, since all the other answers doesn't verify the existence of the url ending with an extension different of .html, this one will check it and will also verify if it's an directory. You just need to change from ```RewriteCond %{REQUEST_URI} !/$``` to ```RewriteCond %{REQUEST_FILENAME} !-d```. – Mateus Neves Apr 25 '16 at 15:55
3

to make sure X.html actually exists before rewriting to it:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.*)$ %{REQUEST_FILENAME}.html
  • condition !-f file does not exist as requested
  • condition !-d directory does not exist as requested
  • condition -f requested file with '.html' extension does exist
oriadam
  • 7,747
  • 2
  • 50
  • 48
  • 1
    This is probably the best answer here, and I guess it works just fine in an htaccess file, but if you're putting this in a virtual hosts block like I was, you need to use "%{DOCUMENT_ROOT}%{REQUEST_FILENAME}" to get apache to use it as a file path, rather than a url. – LadyCailin Apr 09 '20 at 14:08
2

There's a lot of answers here that sort of work, but by far the easiest way since apache 2.4 is to just enable MultiViews, see official docs https://httpd.apache.org/docs/2.4/content-negotiation.html

All I had to do was:

<Directory /var/www/html>
  Options +MultiViews
</Directory>

This means that if I go to my site e.g. example.com/dashboard it just serves dashboard.html without needing any redirects, and it keeps the original URL as it was (so it doesn't append the extension).

I couldn't really find specifics in the docs about which file extensions get priority, so not sure what happens if you have both dashboard.html and dashboard.php living in the directory, or whether it's easy to change the priority order.

  • Building on this, if you don't have access to your `httpd.conf` you can simply add a single line to `.htaccess`: `Options +MultiViews`. – ShawnPConroy Jan 31 '23 at 13:29
1

The above solutions did not work for me,
I found a working solution at:
http://www.garron.me/bits/add-html-extension-nginx-apache-htaccess.html
At the .htaccess section.

RewriteCond %{REQUEST_URI} !^.*\.html$  RewriteCond
%{REQUEST_FILENAME} !-f  RewriteCond %{REQUEST_FILENAME} !-d 
RewriteRule ^(.*)$ $1.html [L,R=301]
maswerdna
  • 315
  • 2
  • 10
remco
  • 11
  • 1
1

My solution for nginx:

forcing .html extension

if ($request_filename !~ ^.+(.html|\/)$) {
    return 301 $scheme://$host$request_uri.html;
}
vgrinko
  • 198
  • 1
  • 1
  • 9
0

I think its the best way to go

RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^([^.]+?)/?$ %{REQUEST_URI}.html [L,R=302]

Ijaz Ahmed Bhatti
  • 736
  • 1
  • 7
  • 26