As of WordPress 3.0 to create pretty permalinks you need to use the following .htaccess file:
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
It’s easy to cargo cult this change, and simply paste the code into your own .htaccess file. And I want to make it easy for you to paste that code into your .htaccess. The goal of this post is to examine what the .htaccess rules are doing, and why they’re doing it. With information like that, you can make better decisions around your site. So let’s break this down line by line.
Line 1:
# BEGIN WordPress
It’s a comment. Easy.
And that wraps up this post. Thanks everyone, click the links below to see Part 2, 3, 4, 5, 6 of the series so that we can get more page views and sell more ads!
No wait, that would be a dumb thing to do… so let’s continue instead.
Line 2:
<IfModule mod_rewrite.c>
IfModule is a test that occurs within Apache, and it tests whether the mod_rewrite.c module is included in Apache. If mod_rewrite.c is included the code between <IfModule> and </IfModule> is run. If mod_rewrite.c isn’t included the code within the if statements doesn’t get run.
Line 3:
RewriteEngine On
RewriteEngine On tells Apache to turn on its Rewrite Rules. That’s all there is to this rule. Also, if you want to turn off all of your rules at once set the value to off.
Line 4:
RewriteBase /
RewriteBase is a bit trickier because it affects how your other rules work, and it’s not immediately intuitive how it works.
When a url comes into Apache it looks something like this:
randomtype.ca/blog/wordpress-htaccess-explained
Before the url hits the rewrite rules, the domain name, and leading slash are stripped from the url:
blog/wordpress-htaccess-explained
At this point, the url is run through all of the rewrite rules that you’ve specified, and changed accordingly.
Once those steps have been completed, the RewriteBase rule is applied. In the case of WordPress that line is RewriteBase /, so the leading slash is added to the url:
/blog/wordpress-htaccess-explained
Because the WordPress RewriteBase uses just a forward slash, it doesn’t look like RewriteBase does a whole lot. So let’s expand with an example. Here’s an .htaccess file that has had it’s RewriteBase changed:
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase /blog RewriteRule ^blog/wordpress-htaccess-explained$ post-12.html </IfModule>
Now if you were to access the url randomtype.ca/blog/wordpress-htaccess-explained, Apache would perform the following steps:
- Strip the domain, and leading slash and pass the following path to the rewrite rules:
blog/wordpress-htaccess-explained - Apply the rewrite rules
- In the example, the single rule matches, and the path becomes
post-12.html
- In the example, the single rule matches, and the path becomes
- Apply the RewriteBase rule to the path to create a path of:
/blog/post-12.html- Apache adds in the forward slash between
blogandpost-12.html - Alternatively, you could write
RewriteBase /blogasRewriteBase /blog/which would accomplish the same thing.
- Apache adds in the forward slash between
- Apache then looks for the content at
/blog/post-12.htmlon the file system. - Assuming the content is found the url
randomtype.ca/blog/wordpress-htaccess-explainedserves up the physical file from/blog/post-12.html
It took me awhile to get my head around how RewriteBase works, especially because the apache documentation isn’t crystal clear on that point. You’ll not use it very frequently, but at least now you have an idea of what it does.
Line 5:
Empty – it does nothing – So easy!
Line 6:
RewriteRule ^index\.php$ - [L]
Now we can get to the meat of the .htaccess file – the RewriteRules. This particular rule is easy to understand: if the path is index.php then perform no replacement (that’s what the dash means), and exit out of the apache rewrite loop.
The final part of the rule [L] tells Apache to stop processing any remaining rules.
While this rule may seem silly, hang on to it in the back of your mind, because we’ll come back around to it.
Line 7:
RewriteCond %{REQUEST_FILENAME} !-f
This rewrite conditional uses a couple of special values, %{REQUEST_FILENAME} is a variable based upon the url you request, and !-f is an apache specific extension added to the regular expression language.
Let’s use an example for what the output of %{REQUEST_FILENAME} might look like. Let’s hit the webpage randomtype.ca/test_page.html. Within apache the value of %{REQUEST_FILENAME} gets set to reflect the physical filepath for our request. When I tested this on our servers the value came out like this: /mnt/stor0-wc0-dfw0/123456/randomtype.ca/test_page.html.
The second part of the RewriteCond would typically be a regular expression, however Apache provides some extensions that are being used. The first character – the exclamation mark (!) – is a negation. The second part -f asks whether the test string (in this case %{REQUEST_FILENAME}) exists or not on the file system.
Taken together this RewriteCond makes the following check:
If a file, taken from the variable %{REQUEST_FILENAME} does not exist on the file system, then return true.
Now RewriteCond is a cumulative rule. That is, you can’t just have a RewriteCond, you also have to have a RewriteRule. In this case the next line (Line 8) is another RewriteCond, so what happens then?
What happens is that the two conditions are combined together with a logical AND, and then the RewriteRule is applied when the two conditions are met.
Line 8:
RewriteCond %{REQUEST_FILENAME} !-d
Line 8 is almost identical to Line 7. But instead uses the -d special case, instead of the -f. The -d asks whether the path it is given is a directory or not.
So in plain english:
If a directory, taken from the variable %{REQUEST_FILENAME} does not exist on the file system, then return true.
Line 9:
RewriteRule . /index.php [L]
This rewrite rule says that every request that goes to your website should be rewritten to instead go to /index.php. A little bit greedy huh!? Now of course Line 7 and Line 8 come into play here, and if the conditions aren’t met, then /index.php isn’t used.
Overall, the 3 lines combine to make the plain english statement:
If the variable %{REQUEST_FILENAME} does not exist as a file on the file system and
if the variable %{REQUEST_FILENAME} does not exist as a directory on the file system,
then send the request to /index.php
So what happens if %{REQUEST_FILENAME} exists on the file system as a directory or file? Well then this rule isn’t applied, and the file is served.
For example when you make a request to the RANDOMTYPE website, the html also tells you to download our favicon with the path: http://randomtype.ca/cms/img/rti-favicon.png. If those two conditionals didn’t exist, then the request would instead be sent to /index.php and you wouldn’t get our favicon.
The cool thing about the WordPress /index.php file is that it picks up what the original url was, and displays a dynamic page for your website. Which is why pretty permalinks work in the first place.
Once again the [L] rule tells apache to stop processing rules.
Now let’s back up the boat a little bit and return to Line 6. What’s the point in having a such a simple rule. Well it turns out that [L] does not stop rule processing, instead Apache skips the remaining rules on its current pass. Then Apache starts another pass from the top of the .htaccess file, and starts reapplying rules. Line 6 exists to pop apache out of that loop, and prevent Line 9 from being re-ran. The exact details can be found here.
Line 10:
</IfModule> simply closes the if statement that was started on Line 2.
Line 11:
Another comment!
And that’s it. A full explanation of the WordPress htaccess file.

Pingback: Redirecting a Request with Path Information « BnmnG
Pingback: WORDPRESS – Using Permalinks
Pingback: The WordPress .htaccess File Explained | RANDOMTYPE Inc. | Casapress.info
Pingback: 6 .htaccess Tips to Clean Up and Speed Up your Website | Randomtype Inc.
Pingback: Show Hidden Files in Finder | Jim Friend