Codex

Interested in functions, hooks, classes, or methods? Check out the new WordPress Code Reference!

User:Amereservant/Editing and Customizing htaccess Indirectly

If you are wanting to know how to add your own custom content/rewrite rules to your WordPress .htaccess file without manually editing the .htaccess file, this information is for you.

Upon recently trying to figure out how to add contents to my WordPress generated .htaccess file, I was surprised at how hard it was to find information on this. Luckily though I came across WP htaccess Control plugin, which showed where and how to hook the necessary action/filter hooks to do what I was trying to do.

So here's a run down of what I found:

WordPress .htaccess - Where its creation begins

One helpful bit of knowledge is knowing where the .htaccess file is created/generated from within WordPress. It all starts with the WP_Rewrite class, which is located in wp-includes/rewrite.php file.

The main class methods of interest are mod_rewrite_rules() (which is responsible for generating the .htaccess file rules), flush_rules() (calls save_mod_rewrite_rules()), and the save_mod_rewrite_rules() (which writes the rules to the .htaccess file).

Getting the contents to be written to your .htaccess file

If you ever wondered where to grab what is going to be written to the .htaccess file, the mod_rewrite_rules filter hook is the one you're probably interested in.

This returns a string of the .htaccess generated rules, which allows for appending/pre-pending your own rules and/or modifying the string.

Here's an example of using this hook:

<?php
/**
 * Get .htaccess contents before being written to file
 *
 * Uncomment the first two lines and to go Settings > Permalinks to see the output.
 */
function my_htaccess_contents( $rules )
{
    // echo '<pre>'. $rules .'</pre>';
    // exit();
    return $rules . "# My little addition!\n";
}
add_filter('mod_rewrite_rules', 'my_htaccess_contents');

The output should be something like this:

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

# My little addition!

That's an example of how we can append our own content to the end of the generated rewrite rules.

Ok, so perhaps you're wondering where the #BEGIN WordPress and #END WordPress lines are at....
Well, the answer is in the wp-admin/includes/misc.php file's save_mod_rewrite_rules() function and it's call to insert_with_markers() (which saves the .htaccess file with the "markers").

Adding your own .htaccess contents via WordPress

If you are wanting to add .htaccess rules or additional directives when WordPress generates the .htaccess file, here's how to do it.

NOTE: If the rewrite rule is an internal rewrite, it shouldn't be added to the .htaccess file, but instead it should be added to and handled by WordPress's internal rewrites. See the Examples for examples of this.

Here's two different ways you might want to add .htaccess file rules...

Add additional non-rewrite .htaccess directives

So say you want to add cache headers based on content, file access restrictions, or whatever else you can find to put in your .htaccess file.
For this, we use the above example for hooking the mod_rewrite_rules filter hook.

Your filter function is passed the generated rules as a string and it MUST be returned.
Here you can choose to return it with your rules prepended or appended to the generated rules. Here's an example of that:

<?php
function my_htaccess_contents( $rules )
{
$my_content = <<<EOD
\n # BEGIN My Added Content
# Protect wpconfig.php
<Files wp-config.php>
    Order Allow,Deny
    Deny from all
</Files>
# END My Added Content\n
EOD;
    return $my_content . $rules;
}
add_filter('mod_rewrite_rules', 'my_htaccess_contents');

This will now make our .htaccess contents look something like this:

# BEGIN My Added Content
# Protect wpconfig.php
<Files wp-config.php>
    Order Allow,Deny
    Deny from all
</Files>
# END My Added Content

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

Add external or non-WordPress rewrite rules

Perhaps you need to add rewrite rules that don't pertain to WordPress' internal rewriting and possibly even point to a completely different URL.

WordPress can also add these rules to your .htaccess file when it generates one!

The best way to add our non-WordPress rewrite rules (thanks Otto) is by using the add_rewrite_rule().

<?php
function add_my_external_rules()
{
    add_rewrite_rule('myrule', '/newlocation');
}
add_action('init', 'add_my_external_rules');

Any rules that don't start with index.php will be added to the .htaccess files as a normal RewriteRule when the rules are flushed.

Here's the example of what our .htaccess file will look like with the added rule..

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteRule ^myrule /newlocation [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
GOTCHA: The internal rewrite rules use $matches[1] for referencing matching patterns.
This doesn't work for .htaccess rules and you must use standard $1 and so forth for those rules.

My rules weren't added to the .htaccess file!

Ok, so I haven't told you how to make WordPress re-generate the .htaccess file with the updated rules/directives ...
There's a couple of ways to do this:

1. Make a "flush_rules" function.

We can create a function that flushes the rules and hook it to the admin_init action hook so it'll only be called when any administrative page is loaded.
This was the solution for the WordPress Support topic: Writing $wp_rewrite->non_wp_rules to .htaccess?
The example there is sufficient enough so I won't repeat it.

2. Visit the Permalink Settings page.

When you visit Permalink Settings and/or change and save the settings, WordPress flushes the rewrite rules and re-generates the .htaccess file to reflect the changes. So this is probably the easiest way to regenerate the .htaccess file once you've added your rewrite hooks as mentioned above.

Multisite .htaccess Rules

WordPress Multisite (aka. Network Site) differs from single instances and the fact that the .htaccess rules apply to the entire network, flushing the rules won't re-generate the .htaccess file.

I am currently unaware of how to add custom .htaccess content/rules at this time since there doesn't appear to be any built-in WordPress functions for this in Multisite mode.

Useful Links

  • WP_Rewrite - The documentation on the WP_Rewrite class is great and very helpful! It's a bit extensive, but very helpful.
  • WP htaccess Control Plugin - The source code in this plugin helped me figure this out. I haven't tried the plugin, but it looks promising.
  • WP_Rewrite Plugin Hooks - This is a list of action/filter hooks you can hook to if you want to alter the WordPress rewrite rules. Very handy.
  • Blog .htaccess Rules - Here's several "most essential .htaccess rules for blogs" that will help with blog performance and might be of interest.
  • .htaccess Rules for Speed - These are some suggested .htaccess rules to help improve your blog/site speed.