Celebrating 20 years online :)
Web Dev + WordPress + Security

Remove or Hide File Extension with .htaccess

A common question I get is how to change or hide file extensions using .htaccess. Apparently search engines prefer “pretty” permalink URL structures over query-strings and file extensions. This is one reason why WordPress provides an SEO-friendly permalink option for URLs; because it is preferred over the default plain query-string based format. From the Permalinks settings screen in the WordPress Admin Area:

WordPress offers you the ability to create a custom URL structure for your permalinks and archives. Custom URL structures can improve the aesthetics, usability, and forward-compatibility of your links.

So rewriting URLs to be more SEO friendly brings many benefits, including better aesthetics, usability, and compatibility of your links. How much impact URL rewriting has on actual search results is open for debate, but either way the benefits are clear. To that end, this tutorial explains two (similar) techniques to help restructure your site’s URLs:

  • How to remove the file extension from URLs (for permalink structure)
  • How to hide or disguise the true extension (by replacing it with another)

These two techniques open the doors to many possibilities for SEO-friendly permalinks, replacing file extensions, and rewriting URLs in general. These are super useful tools for anyone working on Apache server.


In order to play along with the URI-rewriting adventures, you will need the following:

Here is a guide that explains more about creating .htaccess files. We will be working with the .htaccess file that is located in the public root web directory, for example:


So create that file if it does not already exist, and make sure to lock down its file permissions so that nobody can access the file publicly. If an .htaccess file already exists and contains some existing rules, you may want to test on some other site that has a clean .htaccess file. Best to learn this technique on a clean slab, and then implement on other more complex sites once you understand how it works.

Also keep in mind that the examples provided in this tutorial are kept as simple as possible, for the sake of clarity. They work flawlessly in the ideal simplified case, but will require further adaptation and configuration depending on site complexity, file structure, and other factors. In other words, these techniques give you a starting point for further development according to the specific needs of your site.

Remember to test thoroughly!

Note: If you get a 500 “Internal Sever Error” while testing the examples, try enabling FollowSymLinks by adding this line to the top of your .htaccess file: Options +FollowSymlinks

How to Remove File Extensions

This trick actually is one of the more challenging mod_rewrite techniques. Everything is very subtle and requires precise configuration. I’ve done my best to simplify everything as much as possible, but the underlying logic is rather complex. So before diving into the lengthy explanation and theory, let’s look at the code up front and center:

# Remove file extension
# @ https://m0n.co/06
<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /(([^/]+/)*([^/.]+))\.php[\ ?]
	RewriteRule \.php$ /%1/ [R=301,NC,L]
	RewriteRule ^(.*)/$ /$1.php [NC,L]

This is the magic code to remove file extensions from URLs and serve permalink-format URLs instead. As written, this code does two things:

  • Redirects requests for file-name URLs to corresponding permalink URLs
  • Rewrites requests for permalink URLs with corresponding file-name URLs

So both sides of the URL coin are covered. No edits are required for this technique to work. The code simply does what it says: removes file extensions from URLs (and replaces them with permalink-format URLs). There are some conditions that need met, as explained in the next section.

Code Explanation

In general, to remove file extensions and replace them with clean permalink structure, three conditions must be met:

  1. A file must exist for each URL
  2. Requests for the file must be redirected to the permalink URL
  3. Requests for the permalink URL must be rewritten as the file

To further understanding, let’s take a look at each of these conditions..

Condition 1: A file must exist for each URL

The first condition is important. To understand, consider a simple site that contains ONLY the following files:


So if these URLs are requested via browser, that’s how they will look in the address bar. But you, being savvy with SEO and usability, want to remove the file extension from each of those requests, so that they look more user-friendly:


Because each of these permalink URLs corresponds to an existing .html file on the server, the “remove file extension” technique will work. Notice the file name is used as the directory name in each of the permalink URLs. This is key to understanding how it works. So for example, if the following request is made:


The “remove extension” technique will not work. Why? Because there is not an existing file named page4.html that matches the request. Thus the result will be a standard 404 “Not Found” error. Hence the first condition: A file must exist for each URL. But that is not to say that a unique file must exist for each URL. If you are using a dynamic language such as PHP, you can have one file that handles all requests. For example if you have an index.php file that handles all sorts of requests such as:


Then the first condition is met, because there is a file (index.php) to handle each valid URL request. So in this example, after implementing the “remove extension” technique, all URLs that are handled by index.php will be rewritten in permalink format. It works because there is a file to process each request.

Condition 2: Requests for the file must redirect to the permalink URL

The second condition is satisfied by the following Apache rules in our “remove extension” technique:

RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /(([^/]+/)*([^/.]+))\.php[\ ?]
RewriteRule \.php$ /%1/ [R=301,NC,L]

Quite simply, these directives check if the request is for a .php file; if yes, then the request is redirected (via 301 status) to a directory of the same name. This is the main part of the “remove extension” technique: any requests for the file are redirected to the corresponding permalink URL.

Condition 3: Requests for the permalink URL must be rewritten as the file

The third condition is satisfied by the following Apache rule in our “remove extension” technique:

RewriteRule ^(.*)/$ /$1.php [NC,L]

This directive handles the case where the user requests the permalink URL instead of the file-name URL. Basically this rule checks if the request is for the permalink directory name; if yes, then the request is rewritten as the file-name URL. When this rule is combined with those from the 2nd condition, all requests are accounted for: requests for the file will redirect to the permalink URL, and requests for the permalink URL will rewrite as the file-name URL.

How to Hide File Extensions

The “hide extension” technique is very similar to the previous “remove extension” technique. The code basically is the same, with just a few subtle modifications. Here’s the magic formula for hiding (or disguising) file extensions using .htaccess:

# Hide file extension 
# @ https://m0n.co/06
<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /(([^/]+/)*([^/.]+))\.php[\ ?]
	RewriteRule \.php$ /%1.html [R=301,NC,L]
	RewriteRule ^(.*)\.html?$ /$1.php [NC,L]

As written, this code does two things:

  • Redirects requests for .php files to corresponding .html files
  • Rewrites requests for .html files as .php files

Again, both sides of the coin are covered. No edits are required for this technique to work. The code simply does what it says: hides the true file extension (.php) and replaces it with a spoofed file extension (.html). So when someone requests, say, whatever.php in the browser, the address bar will show whatever.html in the address bar. It’s a great way to disguise the true identity of the files on the server. The browser shows the HTML file, while the server sends the PHP file.

Of course, you can replace the PHP and HTML file extensions with whatever extensions actually exist on the server; it doesn’t have to be PHP to HTML. A bit of tweaking and you can hide any file extension and replace it with any other.

Code Explanation

To understand how this code works, check out the Code Explanation for the previous “remove extension” technique. The code is virtually the same, and the same three conditions must be satisfied. The main difference is that, instead of replacing the file extension with permalink/directory structure, this code replaces the file extension with a different extension. If you compare the two code snippets, you can find the differences in the regular expressions.

Wrap Up

Without a doubt, this technique can be confusing. I spent probably way too much time figuring all of this out and writing about it in way that is complete and makes sense. If nothing else, the best way to understand is to add the code to your development site, try some different requests, and examine the results.

About the Author
Jeff Starr = Web Developer. Book Author. Secretly Important.
GA Pro: Add Google Analytics to WordPress like a pro.

2 responses to “Remove or Hide File Extension with .htaccess”

  1. Jim S Smith 2019/10/10 9:14 am

    As usual,

    Very good tutorial!

    I am thinking of doing some serious rework on a past PHP framework project. As part of my initial voyage into the world of PHP, I had built my own web rendering framework in PHP. I think it is now time to begin reworking it to generate/use SEF links.

    Always glad to see some great and informative material.

    – Jim

Comments are closed for this post. Something to add? Let me know.
Perishable Press is operated by Jeff Starr, a professional web developer and book author with two decades of experience. Here you will find posts about web development, WordPress, security, and more »
Digging Into WordPress: Take your WordPress skills to the next level.
Crazy that we’re almost halfway thru 2024.
I live right next door to the absolute loudest car in town. And the owner loves to drive it.
8G Firewall now out of beta testing, ready for use on production sites.
It's all about that ad revenue baby.
Note to self: encrypting 500 GB of data on my iMac takes around 8 hours.
Getting back into things after a bit of a break. Currently 7° F outside. Chillz.
Get news, updates, deals & tips via email.
Email kept private. Easy unsubscribe anytime.