Okay so my question is quite simple. I need to implement some custom routing rules for my plugin. Those routes would only take one argument (so nothing complicated) and would look like : http://www.example.org/myroute/myargument

And ideally, this would call a custom class, and display a custom template (that could directly access the class).

What's the best approach for this ? Cheers

2 Answers 2


You need to do three important things:

  1. Create a custom rewrite rule to turn parts of the URI into values passed to index.php.
  2. Add myroute and myargument to WordPress's query variables whitelist, so that WordPress doesn't just ignore them when they appear in a query string.
  3. Flush the rewrite rules.

Firstly, I'm going to recommend that instead of http://www.example.org/myroute/myargument, you settle on some kind of special prefix or suffix to denote when the URI should be considered one of these special 'routes'. For the the sake of this example, I've chosen the prefix api, so that it would be http://www.example.org/api/myroute/myargument. I chose api because when I did something RESTful, like what you seem to be working on, it was for an API.

The Code

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['api/(.*?)/(.+?)'] ) ) {
        global $wp_rewrite;

// Adding a new rule
function my_insert_rewrite_rules( $rules ) {
    $newrules = array();
    $newrules['api/(.*?)/(.+?)'] = 'index.php?myroute=$matches[1]&myargument=$matches[2]';
    return $newrules + $rules;

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

Quick Breakdown

It's all fairly straight forward. The regex pattern is added to a list of all the rewrite rules in WordPress, and your custom pattern is at the top of the list. When the pattern is matched, WordPress will stop looking through the list of rewrite rules, and use the regex's captured values in place of the references ($matches[1] and $matches[2]) in the query string passed to index.php.

Adding the query variables myroute and myargument to the whitelist just makes WordPress pay attention to them rather than discarding them.

Alternative way of 'namespacing' your custom route

If you wanted to avoid using /api/ as a prefix, you could use a query string variable/field instead. To do something like that, you would change the regex to something like (.*?)/(.+?)\\?api=1 and then add api as an additional parameter to the array_push() call made in my_insert_query_vars().

That would change the custom route so that it triggers any time api=1 is the first element of the query string, e.g. it would trigger for http://example.com/anytext/anytext?api=1.

Ignore the use of the term 'namespacing' - just used it for brevity.

If you don't 'namespace' with either a prefix or a suffix, you will end up with colliding URI patterns. This is because WordPress will have no way to distinguish your custom pattern from one intended to be a post or page. How would WordPress know that myroute is not a taxonomy, term, or a parent page?

Hope this helps.

  • 2
    Handy note: rules defined in my_insert_rewrite_rules follow order of definition! Start with the longest rule first then work down to the simplest, otherwise /api/myroute will override /api/myroute/myargument.
    – emc
    Commented Feb 25, 2014 at 17:42
  • 1
    @npc Thats an important point to be aware of when creating custom rewrite rules, they can collide that way too. In the example above though, thats not a problem because /api/myroute would simply not be a valid path.
    – eddiemoya
    Commented Mar 3, 2014 at 19:37
  • How could someone load a custom template from their plugin directory whenever the example.org/api/myroute/myargument page was requested?
    – Matt Keys
    Commented Sep 21, 2014 at 10:36
  • 1
    Here is the actual and complete solution by the wordpress: codex.wordpress.org/Rewrite_API/add_rewrite_rule Commented Sep 12, 2015 at 20:15

To expand a bit on what eddiemoya did above:

Like the original poster of this question I wanted to create a custom rewrite, and also deliver a custom template for that rewrite page. The code from edditmoya got me started in the right direction, and I added an extra function to serve up my custom template when the page is accessed.

The custom template could be located anywhere, in my case it is stored in the plugin directory.

I also only wanted to check if the rewrite rules needed to be flushed during plugin activation, so I put that on a register_activation_hook

See below for the full example of what I did:

UPDATED simplified based on advice from milo

class Your_Class

    public function init()
        add_filter( 'template_include', array( $this, 'include_template' ) );
        add_filter( 'init', array( $this, 'rewrite_rules' ) );

    public function include_template( $template )
        //try and get the query var we registered in our query_vars() function
        $account_page = get_query_var( 'account_page' );

        //if the query var has data, we must be on the right page, load our custom template
        if ( $account_page ) {
            return PATH_TO_PLUGIN_TEMPLATES_DIR . 'register.php';

        return $template;

    public function flush_rules()


    public function rewrite_rules()
        add_rewrite_rule( 'account/(.+?)/?$', 'index.php?account_page=$matches[1]', 'top');
        add_rewrite_tag( '%account_page%', '([^&]+)' );


add_action( 'plugins_loaded', array( new Your_Class, 'init' ) );

// One time activation functions
register_activation_hook( PATH_TO_PLUGIN_FILE, array( new Your_Class, 'flush_rules' ) );
  • 1
    you can also just use add_rewrite_endpoint, which will generate the rule for you and add the query var in one go. also if you're adding your own rewrite rules, I suggest the add_rewrite_rule function instead of filtering rewrite_rules_array.
    – Milo
    Commented Sep 23, 2014 at 23:31
  • Thanks Milo, I updated the code to use add_rewrite_rule instead of filtering the rewrite array. I looked at add_rewrite_endpoint but I think add_rewrite_tag may be a better fit for my needs. It seems like add_rewrite_endpoint is mostly useful if you want to add extra argument to existing WP rewrites. Correct me if I am wrong here.
    – Matt Keys
    Commented Sep 24, 2014 at 0:03
  • 2
    I like the object-oriented approach. Too many WP developers still don't know how to use OOP. Thanks for trying to restore my faith into PHP developers. ;)
    – Arvid
    Commented Feb 19, 2018 at 17:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.