add_rewrite_rule()WP 2.1.0

Adds a rewrite rule that transforms a URL structure to a set of query vars.

Any value in the $after parameter that isn't 'bottom' will result in the rule being placed at the top of the rewrite rules.

1 time — 0.000001 sec (speed of light) | 50000 times — 0.03 sec (speed of light) | PHP 7.1.11, WP 4.9.8

No Hooks.


null. Nothing.


add_rewrite_rule( $regex, $query, $after );
$regex(string) (required)
Regular expression to match request against.
$query(string|array) (required)
The corresponding query vars for this rewrite rule.
Priority of the new rule. Accepts 'top' or 'bottom'.
Default: 'bottom'



#1 How to register a new rewrite rule

Here is a simple example of how to register a new rewrite rule, and pass it off to a PHP file for rendering:

1 - Setup a rule:

add_action( 'init',  function() {
	add_rewrite_rule( 'myparamname/([a-z0-9-]+)[/]?$', 'index.php?myparamname=$matches[1]', 'top' );
} );

2 - Flush permalinks. Go to WP Admin > Settings > Permalinks > Save. This doesn’t happen automatically after you add this code

3 - Whitelist the query param:

add_filter( 'query_vars', function( $query_vars ) {
	$query_vars[] = 'myparamname';
	return $query_vars;
} );

4 - Add a handler to send it off to a template file:

add_action( 'template_include', function( $template ) {

	if ( get_query_var( 'myparamname' ) ) {
		return get_template_directory() . '/template-name.php'; 

	return $template;

} );

#2 User-friendly URLs for pages

We create a "nutrition" page (nutrition, ID=12) to display various nutrition information on it. Then, this page uses a separate template. The request variables food and variety are passed to the page.

Let's replace the ugly URL: /nutrition?food=mush&variety=semolina with the pretty ones: /nutrition/mush/semolina

add_action( 'init', 'do_rewrite' );

function do_rewrite(){

	add_rewrite_rule( '^(nutrition)/([^/]*)/([^/]*)/?', 'index.php?pagename=$matches[1]&food=$matches[2]&variety=$matches[3]', 'top' );
	// you need to specify ?p=123 if such a rule is created for post 123
	// the first parameter for posts: p or name, for pages: page_id or pagename

	// tell WP that there are new query parameters
	add_filter( 'query_vars', function( $vars ){
		$vars[] = 'food';
		$vars[] = 'variety';
		return $vars;
	} );

When using $matches[], the array keys start with 1, not 0.

Now you need to update the rewrite rules, to do this, just visit the Settings > Permalinks page - the rules will update in the WP options.

You can get the parameters in the page code through the get_query_var() function. For example, link: /nutrition/mush/semolina

echo get_query_var('food');    // mush
echo get_query_var('variety'); // semolina

By default, WordPress does not recognize new query variables used for rewrite. You need to register them with add_rewrite_tag() or through the filter query_vars, if not done, the rewrite rule will not work.

To make a rule work, you must update (reset) the rules in the database. To do this, call function flush_rules(). Or just go to Settings -> Permalinks where flush_rules() will automatically triggered.


#3 Friendly URL for the page (example 2)

Another example of creating an Friendly URL for a static page. First the code:

// rewrite rule for the sitemap page
add_action( 'init', 'rewrite_rule_my' );

function rewrite_rule_my(){
	add_rewrite_rule( '^(sitemap)/([^/]*)/?', 'index.php?pagename=$matches[1]&pagetype=$matches[2]', 'top' );

	add_rewrite_tag( '%pagetype%', '([^&]+)' );

Here we created the Friendly URL for the permanent page /sitemap?pagename=value (pagename is a query variable). After placing this code in theme functions.php, the Friendly URL of the following form will start working: /sitemap/value and in PHP on this page, you can use the variable $wp_query->query_vars['pagetype'], which will contain the value value. Or you can get the value like this: get_query_var('pagetype').


#4 Friendly URL for a child page

In the parameter pagename we need to pass the whole link, not just the page slug. Suppose the URL of our child page is /parent/slug, then:

add_rewrite_rule( '^(parent/slug)/([^/]*)', 'index.php?pagename=$matches[1]&foo=$matches[2]', 'top' );

That is, in the regular expression you should write (parent/slug) instead of parent/(slug).

WP will now begin to understand URLs of the form /parent/slug/value, where value will be available through get_query_var('foo').


#5 Redirect to scripts other than index.php

Using rewrite rules to redirect to scripts other than index.php.

The $redirect argument works differently when redirecting to a non index.php PHP script. In this case, WordPress passes these redirects to .htaccess instead of handling them itself.

For this reason, variables should be written as $1 instead of $matches[1].

add_action( 'init', 'custom_rewrite_rule', 10, 0 );

function custom_rewrite_rule() {
	add_rewrite_rule( 'nutrition/([^/]*)/([^/]*)/?', 'path/to/script.php?food=&variety=', 'top' );

When adding such a rule -- see WP_Rewrite::add_rule() -- WP checks if there is a index.php file in the $redirect parameter -- see property WP_Rewrite::$index -- if it is not there, control is given to WP_Rewrite::add_external_rule() method.

It adds the rewrite rule to a separate non_wp_rules array: see WP_Rewrite::$non_wp_rules. Later data from this array will go directly to .htaccess.

That is, after installing the code above and updating rewrite rules, you will have a new rule (line 7) in .htaccess:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteRule ^nutrition/([^/]*)/([^/]*)/? /path/to/script.php?food=&variety= [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress
Another example: Friendly login link

This is just an example with no practical use. WordPress since version 3.0 automatically redirects to /wp-login.php if we try to go to /login.

If we type into the browser line, we open the login page (the URL will not change - there will be no redirect):

// Let's make count as a request for
add_action( 'init', 'wp_pretty_login' );

function wp_pretty_login() {
	add_rewrite_rule( 'login$', 'wp-login.php', 'top' );

Get in .htaccess:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteRule ^login$ /wp-login.php [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress


  • Global. WP_Rewrite. $wp_rewrite WordPress rewrite component.


Since 2.1.0 Introduced.
Since 4.4.0 Array support was added to the $query parameter.

Code of add_rewrite_rule() WP 6.0.1

function add_rewrite_rule( $regex, $query, $after = 'bottom' ) {
	global $wp_rewrite;

	$wp_rewrite->add_rule( $regex, $query, $after );