How to remove .html from the URL of a static page?
Also, I need to redirect any url with .html to the one without it. (i.e. www.example.com/page.html to www.example.com/page ).
I think some explanation of Jon's answer would be constructive. The following:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
checks that if the specified file or directory respectively doesn't exist, then the rewrite rule proceeds:
RewriteRule ^(.*)\.html$ /$1 [L,R=301]
But what does that mean? It uses regex (regular expressions). Here is a little something I made earlier...
I think that's correct.
NOTE: When testing your .htaccess do not use 301 redirects. Use 302 until finished testing, as the browser will cache 301s. See https://stackoverflow.com/a/9204355/3217306
Update: I was slightly mistaken, . matches all characters except newlines, so includes whitespace. Also, here is a helpful regex cheat sheet
Sources:
http://community.sitepoint.com/t/what-does-this-mean-rewritecond-request-filename-f-d/2034/2
https://mediatemple.net/community/products/dv/204643270/using-htaccess-rewrite-rules
To remove the .html extension from your urls, you can use the following code in root/htaccess :
RewriteEngine on
RewriteCond %{THE_REQUEST} /([^.]+)\.html [NC]
RewriteRule ^ /%1 [NC,L,R]
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^ %{REQUEST_URI}.html [NC,L]
NOTE: If you want to remove any other extension, for example to remove the .php extension, just replace the html everywhere with php in the code above.
Also see this How to remove .html and .php from URLs using htaccess` .
This should work for you:
#example.com/page will display the contents of example.com/page.html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.+)$ $1.html [L,QSA]
#301 from example.com/page.html to example.com/page
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /.*\.html\ HTTP/
RewriteRule ^(.*)\.html$ /$1 [R=301,L]
With .htaccess under apache you can do the redirect like this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)\.html$ /$1 [L,R=301]
As for removing of .html from the url, simply link to the page without .html
page
You will need to make sure you have Options -MultiViews as well.
None of the above worked for me on a standard cPanel host.
This worked:
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.html [NC,L]
For those who are using Firebase hosting none of the answers will work on this page. Because you can't use .htaccess in Firebase hosting. You will have to configure the firebase.json file. Just add the line "cleanUrls": true in your file and save it. That's it.
After adding the line firebase.json will look like this :
{
"hosting": {
"public": "public",
"cleanUrls": true,
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}
Thanks for your replies. I have already solved my problem. Suppose I have my pages under http://www.yoursite.com/html, the following .htaccess rules apply.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /html/(.*).html\ HTTP/
RewriteRule .* http://localhost/html/%1 [R=301,L]
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /html/(.*)\ HTTP/
RewriteRule .* %1.html [L]
</IfModule>
Good question, but it seems to have confused people. The answers are almost equally divided between those who thought Dave (the OP) was saving his HTML pages without the .html extension, and those who thought he was saving them as normal (with .html), but wanting the URL to show up without. While the question could have been worded a little better, I think it’s clear what he meant. If he was saving pages without .html, his two question (‘how to remove .html') and (how to ‘redirect any url with .html’) would be exactly the same question! So that interpretation doesn’t make much sense. Also, his first comment (about avoiding an infinite loop) and his own answer seem to confirm this.
So let’s start by rephrasing the question and breaking down the task. We want to accomplish two things:
Visibly remove the .html if it’s part of the requested URL (e.g. /page.html)
Point the cropped URL (e.g. /page) back to the actual file (/page.html).
There’s nothing difficult about doing either of these things. (We could achieve the second one simply by enabling MultiViews.) The challenge here is doing them both without creating an infinite loop.
Dave’s own answer got the job done, but it’s pretty convoluted and not at all portable. (Sorry Dave.) Łukasz Habrzyk seems to have cleaned up Anmol’s answer, and finally Amit Verma improved on them both. However, none of them explained how their solutions solved the fundamental problem—how to avoid an infinite loop. As I understand it, they work because THE_REQUEST variable holds the original request from the browser. As such, the condition (RewriteCond %{THE_REQUEST}) only gets triggered once. Since it doesn’t get triggered upon a rewrite, you avoid the infinite loop scenario. But then you're dealing with the full HTTP request—GET, HTTP and all—which partly explains some of the uglier regex examples on this page.
I’m going to offer one more approach, which I think is easier to understand. I hope this helps future readers understand the code they’re using, rather than just copying and pasting code they barely understand and hoping for the best.
RewriteEngine on
# Remove .html (or htm) from visible URL (permanent redirect)
RewriteCond %{REQUEST_URI} ^/(.+)\.html?$ [nocase]
RewriteRule ^ /%1 [L,R=301]
# Quietly point back to the HTML file (temporary/undefined redirect):
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^ %{REQUEST_URI}.html [END]
Let’s break it down…
The first rule is pretty simple. The condition matches any URL ending in .html (or .htm) and redirects to the URL without the filename extension. It's a permanent redirect to indicate that the cropped URL is the canonical one.
The second rule is simple too. The first condition will only pass if the requested filename is not a valid directory (!-d). The second will only pass if the filename refers to a valid file (-f) with the .html extension added. If both conditions pass, the rewrite rule simply adds ‘.html’ to the filename. And then the magic happens… [END]. Yep, that’s all it takes to prevent an infinite loop. The Apache RewriteRule Flags documentation explains it:
Using the [END] flag terminates not only the current round of rewrite
processing (like [L]) but also prevents any subsequent rewrite
processing from occurring in per-directory (htaccess) context.
Resorting to using .htaccess to rewrite the URLs for static HTML is generally not only unnecessary, but also bad for you website's performance. Enabling .htaccess is also an unnecessary security vulnerability - turning it off eliminates a significant number of potential issues. The same rules for each .htaccess file can instead go in a <Directory> section for that directory, and it will be more performant if you then set AllowOverride None because it won't need to check each directory for a .htaccess file, and more secure because an attacker can't change the vhost config without root access.
If you don't need .htaccess in a VPS environment, you can disable it entirely and get better performance from your web server.
All you need to do is move your individual files from a structure like this:
index.html
about.html
products.html
terms.html
To a structure like this:
index.html
about/index.html
products/index.html
terms/index.html
Your web server will then render the appropriate pages - if you load /about/, it will treat that as /about/index.html.
This won't rewrite the URL if anyone visits the old one, though, so it would need redirects to be in place if it was retroactively applied to an existing site.
I use this .htacess for removing .html extantion from my url site, please verify this is correct code:
RewriteEngine on
RewriteBase /
RewriteCond %{http://www.proofers.co.uk/new} !(\.[^./]+)$
RewriteCond %{REQUEST_fileNAME} !-d
RewriteCond %{REQUEST_fileNAME} !-f
RewriteRule (.*) /$1.html [L]
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^.]+)\.html\ HTTP
RewriteRule ^([^.]+)\.html$ http://www.proofers.co.uk/new/$1 [R=301,L]
Making my own contribution to this question by improving the answer from #amit-verma (https://stackoverflow.com/a/34726322/2837434) :
In my case I had an issue where RewriteCond %{REQUEST_FILENAME}.html -f was triggering (believing the file existed) even when I was not expecting it :
%{REQUEST_FILENAME}.html was giving me /var/www/example.com/page.html for all these cases :
www.example.com/page (expected)
www.example.com/page/ (also quite expected)
www.example.com/page/subpage (not expected)
So the file it was trying to load (believing if was /var/www/example.com/page.html) were :
www.example.com/page => /var/www/example/page.html (ok)
www.example.com/page/ => /var/www/example/page/.html (not ok)
www.example.com/page/subpage => /var/www/example/page/subpage.html (not ok)
Only the first one is actually pointing to an existing file, other requests were giving me 500 errors as it kept believing the file existed and appending .html repeatedly.
The solution for me was to replace RewriteCond %{REQUEST_FILENAME}.html -f with RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f
Here is my entire .htaccess (I also added a rule to redirect the user from /index to /) :
# Redirect "/page.html" to "/page" (only if "/pages.html" exists)
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{THE_REQUEST} /(.+)\.html [NC]
RewriteRule ^(.+)\.html$ /$1 [NC,R=301,L]
# redirect "/index" to "/"
RewriteRule ^index$ / [NC,R=301,L]
# Load "/page.html" when requesting "/page" (only if "/pages.html" exists)
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f
RewriteRule ^ /%{REQUEST_URI}.html [QSA,L]
Here is a result example to help you understand all the cases :
Considering I have only 2 html files on my server (index.html & page.html)
www.example.com/index.html => redirects to www.example.com
www.example.com/index => redirects to www.example.com
www.example.com => renders /var/www/example.com/index.html
www.example.com/page.html => redirects to www.example.com/page
www.example.com/page => renders /var/www/example.com/page.html
www.example.com/page/subpage => returns 404 not found
www.example.com/index.html/ => returns 404 not found
www.example.com/page.html/ => returns 404 not found
www.example.com/test.html => returns 404 not found
No more 500 errors 🚀
Also, just to help you debug your redirections, consider disabling the network cache in your browser (as old 301 redirections my be in cache, wich may cause some headaches 😅):
first create a .htaccess file and set contents to -
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html
next remove .html from all your files eg. test.html became just test and also if you wanna open a file from another file then also remove .html from it and just file name
Use a hash tag.
May not be exactly what you want but it solves the problem of removing the extension.
Say you have a html page saved as about.html and you don't want that pesky extension you could use a hash tag and redirect to the correct page.
switch(window.location.hash.substring(1)){
case 'about':
window.location = 'about.html';
break;
}
Routing to yoursite.com#about will take you to yoursite.com/about.html. I used this to make my links cleaner.
To remove the .html extension from your URLs, you can use the following code in root/htaccess :
#mode_rerwrite start here
RewriteEngine On
# does not apply to existing directores, meaning that if the folder exists on server then don't change anything and don't run the rule.
RewriteCond %{REQUEST_FILENAME} !-d
#Check for file in directory with .html extension
RewriteCond %{REQUEST_FILENAME}\.html !-f
#Here we actually show the page that has .html extension
RewriteRule ^(.*)$ $1.html [NC,L]
Thanks
For this, you have to rewrite the URL from /page.html to /page
You can easily implement this on any extension like .html .php etc
RewriteRule ^(.*).html$ $1.html [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.html [NC,L]
You will get a URL something like this:
example.com/page.html to example.com/page
Please note both URLs below will be accessible
example.com/page.html and example.com/page
If you don't want to show page.html
Try this
RewriteRule ^(.*).html$ $1 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.html [NC,L]
More info here
If you have a small static website and HTML files are in the root directory.
Open every HTML file and make the next changes:
Replace href="index.html" with href="/".
Remove .html in all local links. For example: "href="about.html"" should look like "href="about"".
RewriteEngine On
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /html/(.*).html\ HTTP/
RewriteRule .* https://example.com/html/%1 [R=301,L]
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /html/(.*)\ HTTP/
RewriteRule .* %1.html [L]
it might work because its working in my case
RewriteRule /(.+)(\.html)$ /$1 [R=301,L]
Try this :) don't know if it works.
I am trying to apply mod rewrites to my URL to make it nicer but I am getting caught up in all the confusion of this subject.
Please could you help me out with the following examples of what I want to do :
I want to attach any variable from the root of my site into the lyprofile.php page
# Turn mod_rewrite on
RewriteEngine On
RewriteCond %{THE_REQUEST} /(.*)
RewriteRule /(.*) lyprofile.php?us=$1
I want a url such as profile/settings to go to lysettings.php
# Turn mod_rewrite on
RewriteEngine On
RewriteCond %{THE_REQUEST} /profile/settings
RewriteRule /profile/settings lysettings.php
These two examples if working should help me to work out all my other URL's, but I can't get these to work.
Also do you need an absolute URL, as I'm working on my local machine and an absolute URL would just cause a lot of hassle. Just in case my absolute URL is http://localhost/Linku2.5/
You generally want to go from the most specific rules to the least specific. Of the two things that you want to do, the first is the least specific, as (.*) can be anything. So we have to start with the much more specific and less arbitrary /profile/settings.
If these rules are in your htaccess file (in your case, in the Link2.5 directory), you don't want a leading slash, so simply:
# you only need to turn on once
RewriteEngine On
# Don't need absolute URLs, but you may need this line:
RewriteBase /Link2.5/
RewriteRule ^profile/settings lysettings.php [L]
Then, because your other rule is so general, you need to add some conditions so that you don't cause an infinite loop (the rewrite engine will continue to loop until the URI stops changing):
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^(.+)$ lyprofile.php?us=$1 [L,QSA]
If you know your "us" variable can only be numbers or letters, you can make the line with the rule a bit more specific:
RewriteRule ^([A-Za-z0-9]+)$ lyprofile.php?us=$1 [L,QSA]
etc.
Can you check this in vhosts config?
# Turn mod_rewrite on
RewriteEngine On
RewriteRule lyprofile\.php - [L]
RewriteCond %{REQUEST_URI} /(.*)
RewriteRule /(.*) lyprofile.php?us=$1 [L]
RewriteCond %{REQUEST_URI} /profile/settings
RewriteRule /profile/settings lysettings.php [L]
So I've been stuck on this a bit, and its more or less put a top on my project.
Just started up at a new job, and my current assignment is to create an API system for a new project we're launching. Its based on a modified version of wordpress, and my objective here is to take any request that goes to /contrib/api and instead have it direct to a file I have setup to sort out the incoming URL and redirect accordingly.
Here's what I currently have:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /contrib/
RewriteCond %{REQUEST_URI} ^/api/
RewriteRule ^/content/apis/apis\.php$ - [L]
RewriteRule ^index\.php$ - [L]
# uploaded files -- no longer needed since we're serving files directly via apache
# RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L]
# add a trailing slash to /network and /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin/network$ $1wp-admin/network/ [R=301,L]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) wordpress/$2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ wordpress/$2 [L]
RewriteRule . /contrib/index.php [L]
</IfModule>
This was all here before me, except for near the top:
RewriteCond %{REQUEST_URI} ^/api/
RewriteRule ^/content/apis/apis\.php$ - [L]
My goal, as stated, is to take any request from /contrib/api/ and redirect it to my file instead of continuing. Instead, it seems to skip on and go ahead and point to the wp-admin (after a redirect I think). Regardless, it seems my command isn't getting caught, and I'm not sure why.
I thought that the commands are processed first come, but if its continuing on, would moving my code to the end make it work?
I know this is an easy thing to test, but I was hoping I could get some insight on what I'm doing/doing wrong, plus I don't yet have access to the server to actually upload/test code without going through my boss, and I'm thinking 2 weeks in, probably not a good idea to ask my boss to test code every 5 minutes, heh :(
EDIT: As per Bob Vale's notice, fixed the path issue.
You don't need the RewriteCond as the rule will be doing the match anyhow, you just need
RewriteRule ^contrib/api/ /content/apis/apis.php [L]
This will rewrite any url starting with contrib/api to the file /content/apis/apis.php
After many hours of researching this site (and google) I've decided I need help with this problem I'm having. I'm using a snippet of code in my htaccess file that allows for a url to be accessed by either including the .php extension (like this www.mysite.com/about.php ), leaving the extension off completely with no slash (like this www.mysite.com/about ), or adding a slash at the end in place of the extension (like this www.mysite.com/about/ ).
So that part works beautifully. However it still shows the .php extension in the address bar after the page loads whether the user inputted it or not. So far I'm pretty happy with what it's doing as is, but I'd really just like to be able to hide the extension and even go so far as to put a slash at the end and for somereason nothing I'm doing is working in that respect. Hopefully some of this made sense.
I currently have this in my htaccess file.
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^([^/]+)/$ http://mysite.com/test-server/$1.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
RewriteRule (.*)$ http://mysite.com/test-server/$1/ [R=301,L]
This is actually a bad approach SEO-wise because your content is accessible via multiple URLs. Either enforce extensions or don’t.
I prefer extension-less URLs as it for some bizarre reason I want to switch technology stack (i.e. to Rails) I’m not stuck with “.php” on the end of my URLs.
To achieve this, you can just rewrite requests for the extension-less request to a script with “.php” on the end. In your .htaccess file place the following:
RewriteEngine on
# redirect to extension-less URL if requested
RewriteCond %{THE_REQUEST} ^[A-Z]+\s.+\.php\sHTTP/.+
RewriteRule ^(.+)\.php $1 [R=301,L]
I also found this bit of code that works for me quite well at removing the extension, but I've only got it working at the root level so far. I'd like to be able to mod it for different directories within my test site since url structure is really important for this particular project. Nothing but errors when I do that though.
AddType text/x-component .htc
RewriteEngine On
RewriteBase /
# remove .php; use THE_REQUEST to prevent infinite loops
RewriteCond %{THE_REQUEST} ^GET\ (.*)\.php\ HTTP
RewriteRule (.*)\.php$ $1 [R=301]
# remove index
RewriteRule (.*)/index$ $1/ [R=301]
# remove slash if not directory
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} /$
RewriteRule (.*)/ $1 [R=301]
# add .php to access file, but don't redirect
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteCond %{REQUEST_URI} !/$
RewriteRule (.*) $1\.php [L]
What I'm trying to achive is to have all urls on my page look like http://domain.com/page/, no extensions, but a trailing slash. If a user happends to write http://domain.com/page or http://domain.com/page.php it will redirect to the first url. After some googling i found this code, and it's close to working, but when you leave out the trailing slash in your request the url becomes something like http://domain.com/Users/"..."/page/ and therefor returns a 404.
My .htaccess looks like this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{THE_REQUEST} ^GET\ /[^?\s]+\.php
RewriteRule (.*)\.php$ /$1/ [L,R=301]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)/$ $1.php [L]
RewriteCond %{REQUEST_FILENAME} !(.*)/$
RewriteRule (.*)/$ $1.php [L]
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule .*[^/]$ $0/ [L,R=301]
I've been trying to add an additional rule but I really don't get any of this and I haven't been able to find any answers.
For a scenario like this one, the .htaccess author has to consider both what the browser URL bar should display and what file the web server should return/execute. Note also that each external redirect starts the processing of the rewrite directives over.
With that in mind, start by taking care of which file is returned when the URL is in the correct format:
RewriteEngine on
RewriteRule ^/?$ /index.php [L]
RewriteRule ([^./]+)/$ /$1.php [L]
Then, deal with URLs with no trailing slash by redirecting them with [R=301]:
RewriteRule ^/(.*)\.[^.]*$ http://www.example.com/$1/ [R=301,L]
RewriteRule ^/(.*)$ http://www.example.com/$1/ [R=301,L]
Note that the first of these two rules should also take care of the case where there is a filename (like something.php) but also a trailing slash by eliminating the filename extension and re-adding the slash.
Keep in mind that, if your internal directory structure does not match what the web server is serving (as is often the case in shared hosting scenarios), you will likely need to add a RewriteBase directive immediately after the RewriteEngine directive. See the Apache docs for an explanation.