How to Remove URL Extension and Force Trailing Slash in .htaccess

by Radu

I have a small presentation site on WordPress, but I decided to recreate it with HTML and Bootstrap, which is much better.

While it’s much better, it’s also a bit harder because you have to take care of things yourself, such as removing the URL extensions (e.g. .html, .php), adding a trailing slash at the end, and performing proper redirects.

I thought it would be easy to find a proper code for .htaccess, but it wasn’t. I think I spent a couple of hours finding something that works properly.

One code was removing my .html extension, adding the trailing slash at the end, but wasn’t forcing it by performing a redirect.

From this:


to this:


Another code was removing the extension, adding a trailing slash, performing the redirect, but for some reason, it was still possible to manually access the links with the .html extension. And so on…

It’s important to have a proper redirection to avoid duplicate content issues.

Google’s John Mueller also recommends being consistent and either use a trailing slash at the end or not.

Note that trailing slashes on hostnames (e.g. https://example.com/) don’t matter, as it’s explained here.

You can also use a canonical URL to solve duplicate content issues, but a redirection is your best bet, especially if you want to pass the link juice from one link to another.

That being said, I’m writing this post to, hopefully, spare you a lot of time searching for a proper .htaccess code that takes care of all 3 things.

Remove URL Extension and Force Trailing Slash

I’ve used this code to remove the .html extension, but it works for other extensions as well, such as .php, which was the one used by the person who provided the code on Stack Overflow.

So, add this code in your .htaccess file and replace the URL extension with your own.

## Remove html extension

RewriteEngine On
RewriteBase /

# To externally redirect /dir/foo.html to /dir/foo
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s([^.]+)\.html [NC]
RewriteRule ^ %1/ [R=301,L]

# Add a trailing slash at the end    
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !/$
RewriteRule . %{REQUEST_URI}/ [L,R=301]

# To internally forward /dir/foo to /dir/foo.html
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.*?)/?$ $1.html [L]

This code will:

  • Remove the URL extension;
  • Add a trailing slash at the end;
  • Force the trailing slash with a 301 redirect to avoid duplicate content issues.

Note that the original code from the link doesn’t have a 301 redirect added in the first rule (i.e. [R=301,L]).

It has [R,L], which will perform a 302 redirect, which is a temporary one, not a permanent one like 301.

In cases like this, it’s best to use 301 to tell search engines that the link is permanently moved.

If You’re Having Problems with Your Content

After you add the code, clear your browser’s cache or check the site using an Incognito/Private window.

Except for your homepage, your CSS styles will probably be missing and you’ll only have the HTML formatting.

The best way (check update below) to solve this is to add this somewhere in your <head> section:

<base href="/">

Otherwise, you’ll have to use absolute URL paths in your code to solve the issue.

For example, this:

<link href="https://example.com/path/to/style.css" rel="stylesheet">

instead of this:

<link href="path/to/style.css" rel="stylesheet">

Or, if your base is a subdirectory, you can use it like this:

<link href="../style.css" rel="stylesheet">


I’ve done the same thing for my first project, which is based on PHP.

But, the problem is that the <base> tag doesn’t work with things like anchors (<a href="#example">) or query strings (example.com/page/?id=1).

If you have a PHP page that generates query strings, it will be redirected to the base (example.com/?id=1).

So, you either have to use absolute URLs, or find a workaround to keep using base with relative URLs.

I went ahead and created a constant, added it in a config.php file, and used that in my paths to create absolute URLs and finish with the nuisance once and for all.

This is the constant:

define("BASE_URL", "https://example.com/")

This is how I used it in the links:

<img src="<?php echo BASE_URL; ?>assets/images/image.png">

Should have done that from the start, but I’m still a rookie and still learning from my mistakes. 😃

That’s a Wrap

I hope this post helped you out, and you managed to remove the URL extension and force a trailing slash at the end of it.

If some info is outdated or incorrect, or you have anything to add, say or ask, please contact me via Twitter or email.

About Radu

I've been working online, from home, for over 9 years. I learned a lot of different stuff related to websites. My main expertise is WordPress, but for some time, I started focusing more on web development.