Ready to get started?Download WordPress


Class Reference/WP Rewrite

This document assumes familiarity with Apache's mod_rewrite. If you've never heard of this before, try reading Sitepoint's Beginner's Guide to URL Rewriting. Also see Otto's explanation of hierarchy of rewrite rules in the wp-hackers email list.


Role of WP_Rewrite

WP_Rewrite is WordPress' class for managing the rewrite rules that allow you to use Pretty Permalinks feature. It has several methods that generate the rewrite rules from values in the database. It is used internally when updating the rewrite rules, and also to find the URL of a specific post, Page, category archive, etc.. It's defined in wp-includes/rewrite.php as a single instance global variable, $wp_rewrite, is initialised in wp-settings.php.

Methods and Properties

This is the formal documentation of WP_Rewrite. Try not to access or set the properties directly, instead use the methods to interact with the $wp_rewrite object. See also Rewrite_API.


The permalink structure as in the database. This is what you set on the Permalink Options page, and includes 'tags' like %year%, %month% and %post_id%.
Anything to be inserted before category archive URLs. Defaults to 'category/'.
Structure for category archive URLs. This is just the $category_base plus '%category%'.
Anything to be inserted before author archive URLs. Defaults to 'author/'.
Structure for author archive URLs. This is just the $author_base plus '%author%'.
Anything to be inserted before pagination indices. Defaults to 'page/'.
Supported feeds names (rdf, rss, atom) Use add_feed to override or add.
Anything to be inserted before feed URLs. Defaults to 'feed/'.
Structure for feed URLs. This is just the $feed_base plus '%feed%'.
Anything to be inserted before searches. Defaults to 'search/'.
Structure for search URLs. This is just the $search_base plus '%search%'.
Anything to be inserted just before the $feed_structure to get the latest comments feed. Defaults to 'comments'.
The structure for the latest comments feed. This is just $comments_base plus $feed_base plus '%feed%'.
Structure for dated archive URLs. Tries to be '%year%/%monthnum%/%day%', '%day%/%monthnum%/%year%' or '%monthnum%/%day%/%year%', but if none of these are detected in your $permalink_structure, defaults to '%year%/%monthnum%/%day%'. Various functions use this structure to obtain less specific structures: for example, get_year_permastruct() simply removes the '%monthnum%' and '%day%' tags from $date_structure.
Structure for Pages. Just '%pagename%'.
Anything up to the start of the first tag in your $permalink_structure.
The root of your WordPress install. Prepended to all structures.
Used internally when calculating back references for the redirect part of the rewrite rules.
The rewrite rules. Set when rewrite_rules() is called.
Associative array of "rules that don't redirect to WP's index.php (and thus shouldn't be handled by WP at all)" roughly in the form 'Pattern' => 'Substitution' (see below).
An array of all the tags available for the permalink structure. See Using Permalinks for a list.
What each tag will be replaced with for the regex part of the rewrite rule. The first element in $rewritereplace is the regex for the first element in $rewritecode, the second corresponds to the second, and so on.
What each tag will be replaced with in the rewrite part of the rewrite rule. The same correspondance applies here as with $rewritereplace.


add_rewrite_tag($tag, $pattern, $query) 
Add an element to the $rewritecode, $rewritereplace and $queryreplace arrays using each parameter respectively. If $tag already exists in $rewritecode, the existing value will be overwritten.
See also: add_rewrite_tag($tagname, $regex)
add_feed($feedname, $function
Add a new feed type like /atom1/ . Requires one-time use of flush_rules() to take effect.
Regenerate the rewrite rules and save them to the database
generate_rewrite_rule($permalink_structure, $walk_dirs = false) 
Generates a no-frills rewrite rule from the permalink structure. No rules for extra pages or feeds will be created.
generate_rewrite_rules($permalink_structure, $ep_mask = EP_NONE, $page = true, $feed = true, $forcomments = false, $walk_dirs = true) 
A large function that generates the rewrite rules for a given structure, $permalink_structure. If $page is true, an extra rewrite rule will be generated for accessing different pages (e.g. /category/tech/page/2 points to the second page of the 'tech' category archive). If $feed is true, extra rewrite rules will be generated for obtaining a feed of the current page, and if $forcomments is true, this will be a comment feed. If $walk_dirs is true, then a rewrite rule will be generated for each directory of the structure provided, e.g. if you provide it with '/%year%/%month%/%day/', rewrite rules will be generated for '/%year%/', /%year%/%month%/' and '/%year%/%month%/%day%/'. This returns an associative array using the regex part of the rewrite rule as the keys and redirect part of the rewrite rule as the value.
get_date_permastruct(), get_category_permastruct(), get_date_permastruct() etc. 
Populates the corresponding property (e.g., $date_structure for get_date_permastruct()) if it's not already set and returns it. The functions get_month_permastruct() and get_year_permastruct() don't have a corresponding property: they work out the structure by taking the $date_structure and removing tags that are more specific than they need (i.e., get_month_permastruct() removes the '%day%' tag, as it only needs to specify the year and month).
Set up the object, set $permalink_structure and $category_base from the database. Set $root to $index plus '/'. Set $front to everything up to the start of the first tag in the permalink structure. Unset all other properties.
returns a string (not an array) of all the rules. They are wrapped in an Apache <IfModule> block, to ensure mod_rewrite is enabled.
Returns the set of rules for any Pages you have created.
populate and return the $rules variable with an associative array as in generate_rewrite_rules(). This is generated from the post, date, comment, search, category, authors and page structures.
Change the category base.
Change the permalink structure.
Returns true if your blog is using PATHINFO permalinks.
Returns true your blog is using "pretty" permalinks via mod_rewrite.
Returns true if your blog is using any permalink structure (i.e. not the default query URIs ?p=n, ?cat=n).
WP_Rewrite (constructor) 
Calls init().
returns the array of rewrite rules as in rewrite_rules(), but using $matches[xxx] in the (where xxx is a number) instead of the normal mod_rewrite backreferences, $xxx (where xxx is a number). This is useful when you're going to be using the rules inside PHP, rather than writing them out to a .htaccess file.

Plugin Hooks

As the rewrite rules are a crucial part of your weblog functionality, WordPress allows plugins to hook into the generation process at several points. rewrite_rules(), specifically, contains nine filters and one hook for really precise control over the rewrite rules process. Here's what you can filter in rewrite_rules():

mod_rewrite_rules() is the function that takes the array generated by rewrite_rules() and actually turns it into a set of rewrite rules for the .htaccess file. This function also has a filter, mod_rewrite_rules, which will pass functions the string of all the rules to be written out to .htaccess, including the <IfModule> surrounding section. (Note: you may also see plugins using the rewrite_rules hook, but this is deprecated).


(See also: Permalinks for Custom Archives) The most obvious thing a plugin would do with the $wp_rewrite object is add its own rewrite rules. This is remarkably simple. Filter the generic rewrite_rules_array.

A Quick and dirty example for rewriting http://mysite/project/1 into http://mysite/index.php?pagename=project&id=1:

add_filter( 'rewrite_rules_array','my_insert_rewrite_rules' );
add_filter( 'query_vars','my_insert_query_vars' );
add_action( 'wp_loaded','my_flush_rules' );

// flush_rules() if our rules are not yet included
function my_flush_rules(){
	$rules = get_option( 'rewrite_rules' );

	if ( ! isset( $rules['(project)/(\d*)$'] ) ) {
		global $wp_rewrite;

// Adding a new rule
function my_insert_rewrite_rules( $rules )
	$newrules = array();
	$newrules['(project)/(\d*)$'] = 'index.php?pagename=$matches[1]&id=$matches[2]';
	return $newrules + $rules;

// Adding the id var so that WP recognizes it
function my_insert_query_vars( $vars )
    array_push($vars, 'id');
    return $vars;

Keep in mind that the flush_rules function is quite slow, so in practice you never want to call it from the wp_loaded action that gets executed on each page load. Instead, call this function only when the rewrite rules change. If the rules get set up and then never change, it is enough to flush in register_activation_hook of your plugin.

The Jerome's Keywords plugin does this to enable URLs like http://example.com/tag/sausages.

function keywords_create_rewrite_rules( $rewrite ) {
	global $wp_rewrite;
	// add rewrite tokens
	$keytag_token = '%tag%';
	$wp_rewrite->add_rewrite_tag( $keytag_token, '(.+)', 'tag=' );
	$keywords_structure = $wp_rewrite->root . "tag/$keytag_token";
	$keywords_rewrite = $wp_rewrite->generate_rewrite_rules( $keywords_structure );
	return ( $rewrite + $keywords_rewrite );

Instead of inserting the rewrite rules into the $rewrite array itself, Jerome chose to create a second array, $keywords_rewrite, using the WP_Rewrite function generate_rewrite_rules(). Using that function means that the plugin doesn't have to create rewrite rules for extra pages (like page/2), or feeds (like feed/atom), etc. This array is then appended onto the $rewrite array and returned.

A simpler example of this is Ryan Boren's Feed Director plugin. This simply redirects URLs like http://example.com/feed.xml to http://example.com/feed/rss2:

function feed_dir_rewrite( $wp_rewrite ) {
    $feed_rules = array(
        'index.rdf' => 'index.php?feed=rdf',
        'index.xml' => 'index.php?feed=rss2',
        '(.+).xml' => 'index.php?feed=' . $wp_rewrite->preg_index(1)

    $wp_rewrite->rules = $feed_rules + $wp_rewrite->rules;
    return $wp_rewrite->rules;

// Hook in.
add_filter( 'generate_rewrite_rules', 'feed_dir_rewrite' );

As the array is so simple here, there is no need to call generate_rewrite_rules(). Again, the plugin's rules are added to WordPress'. Notice that as this function filters generate_rewrite_rules, it accepts a reference to the entire $wp_rewrite object as a parameter, not just the rewrite rules.

Of course, as you're adding your rewrite rules to the array before WordPress does anything with them, your plugins rewrite rules will be included in anything WordPress does with the rewrite rules, like write them to the .htaccess file.

Non-WordPress rewrite rules

->non_wp_rules = array( 'Pattern' => 'Substitution' );


 <IfModule mod_rewrite.c>
 RewriteEngine On
 RewriteBase /wp_home/
 RewriteRule ^Pattern /wp_home/Substitution [QSA,L]
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d
 RewriteRule . /wp_home/index.php [L]

where /wp_home/ is WordPress' home directory (or the root URL / if WordPress is installed in your web root.)



  • Lesson: Rewrite API Overview
  • Class: WP_Rewrite() - An overview of WordPress's built-in URL rewrite class.



Further Reading

See also index of Class Reference and index of Function Reference.