Rewriting Basics

URL rewriting is the process of creating a URL that is friendly for users to look at, type, and share and is usually referred to as a pretty URL. URL rewriting allows you to take query parameters and add them to the URL instead of after the ? in the URL. WordPress already does this for the built in taxonomies such as going to a post or page. When you enable permalinks in WordPress and then go to a post url such as www.mysite.com/my-favorite-post, the real page that gets loaded is www.mysite.com/index.php?p=1. WordPress also uses rewrites for pagination. When you go to page 2 of a page such as www.mysite.com/page/2 the real url you are directed to is www.mysite.com/index.php?paged=2. Custom rewrite rules can be used to add variables to the url without using query parameters, which can be very useful in plugin development.

htaccess

It all starts in the .htaccess file that WordPress updates when you enable permalinks, note that it directs urls that can’t be found to index.php. So when you navigate to www.mysite.com/page/2, the page directory is not found and you are redirected to /index.php. The .htaccess file WordPress creates looks something like this.

# 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

The index.php file then parses the URL that was called, in this example www.mysite.com/page/2 and runs it through a series of regexes to determine what the query variables are. WordPress has a rewrite rule regex for pagination that looks like this:

page/?([0-9]{1,})/?$

So any URL that matches that regex is converted to index.php?&paged=$matches[1].

Rewrite Rules

Adding a rewrite rule can be done with the add_rewrite_rule() function. The function takes three parameters: the first is the regex that will match the rule, the second is the rewritten URL, the third is whether to place the rule at the top or the bottom of the list of rewrite rules. To add a rewrite rule that will recognize “/pageOrPostName/MyNewVar/anyValue” at the end of a url, we could add the following rule.

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

One final note here is that for the rewrite rule to actually be added and used by WordPress, the flush_rewrite_rules() function needs to be called.

flush_rewrite_rules();

Rewrite Tags

In order to use the WordPress get_query_var() function, the variable name needs to be registered with WordPress. This is a simple process and can be done in a few different ways. The simplest method is to just use the add_rewrite_tag() function before registering the rewrite rule. This function takes two parameters, the name of the tag surrounded by % characters, and the regex that will match the parameter. The benefit of this method is that the tag can now be used in the WordPress custom permalinks option under the permalinks settings page.

add_rewrite_tag( '%newVariableName%', '([^/]*)' );

Another option for registering the query variable is to use the ‘query_vars’ filter and add the variable to the array of query variables. This is also very easy and looks like this:

/**
 * add query vars
 *
 * @author  Joe Sexton <joe@webtipblog.com>
 * @param   array $vars
 * @return  array
 */
function js_add_query_vars_filter( $vars ){

    $vars[] = "newVariableName";
    return $vars;
}
add_filter( 'query_vars', 'js_add_query_vars_filter' );

Implementing URL Rewriting

It is important to note that URL rewriting, or more specifically flushing the rewrite rules is a time intensive activity and should not be done on every page load. WordPress recommends adding this task to a plugin activation hook, but I have found that a user may flush their rewrite rules manually and clear out the plugin rules in doing so. This function will check if the rule exists on both plugin activation and init, and add it if it doesn’t. This way it will only flush the rules when needed, not on every page load.

/**
 * js_update_rewrite_rules
 *
 * @author    Joe Sexton <joe@webtipblog.com>
 */
function js_update_rewrite_rules() {

    add_rewrite_tag( '%newVariableName%', '([^/]*)' );

    $rewrite_rules = get_option( 'rewrite_rules' );

    $newVarRegex = '^([^/]*)/MyNewVar/([^/]*)/?';

    // check if the rule exists
    if ( !isset( $rewrite_rules[$newVarRegex] ) ) {

        add_rewrite_rule(
            $newVarRegex,
            'index.php?pagename=$matches[1]&newVariableName=$matches[2]',
            'top'
        );

        flush_rewrite_rules();
    }
}

/**
 * on activation
 *
 * @author  Joe Sexton <joe@webtipblog.com>
 */
function js_on_activation() {

    js_update_rewrite_rules();
}
register_activation_hook( __FILE__, 'js_on_activation' );

/**
 * on init
 *
 * @author  Joe Sexton <joe@webtipblog.com>
 */
function js_on_init() {

    js_update_rewrite_rules();
}
add_action( 'init', 'js_on_init' );

Once this is done, the variable can be retrieved on a page using the get_query_var() function like this:

$variable = get_query_var( 'newVariableName' );

One comment on “Update URL Rewrite Rules in WordPress

  1. Harald says:

    Hello I have a problem rewriting query strings in WordPress.

    I tried several solutions but it still isn’t working.

    For example, http://haraldonline.nl/haraldheukers?city=amsterdam must redirect to
    http://haraldonline.nl/haraldheukers/amsterdam. What I prefer is that de last slash has to be replaced with a dash:
    http://haraldonline.nl/haraldheukers-amsterdam

    I tried this code in the theme functions.php

    /**
    * js_update_rewrite_rules
    *
    * @author Joe Sexton
    */
    function js_update_rewrite_rules() {

    add_rewrite_tag( ‘%city%’, ‘([^/]*)’ );

    $rewrite_rules = get_option( ‘rewrite_rules’ );

    $newVarRegex = ‘^([^/]*)/([^/]*)/?’;

    // check if the rule exists
    if ( !isset( $rewrite_rules[$newVarRegex] ) ) {

    add_rewrite_rule(
    $newVarRegex,
    ‘index.php?pagename=$matches[1]&city=$matches[2]’,
    ‘top’
    );

    flush_rewrite_rules();
    }
    }

    /**
    * on activation
    *
    * @author Joe Sexton
    */
    function js_on_activation() {

    js_update_rewrite_rules();
    }
    register_activation_hook( __FILE__, ‘js_on_activation’ );

    /**
    * on init
    *
    * @author Joe Sexton
    */
    function js_on_init() {

    js_update_rewrite_rules();
    }
    add_action( ‘init’, ‘js_on_init’ );

    Please help

Comments are closed.