I am having some issues with rewrite rules. I have a URL like this:


Which I want to treat via query vars like


I have this tag and rule set:

add_action('init', 'custom_rewrite_tag', 10, 0);
function custom_rewrite_tag() {
    add_rewrite_tag('%section%', '([^(&)]+)');

add_action('init', 'custom_rewrite_rule', 10, 0);
function custom_rewrite_rule() {
    add_rewrite_rule('post-type/(.+?)/(.+?)/(.+?)/?$', 'index.php?pagename=$matches[1]/$matches[2]&section=$matches[3]','top');

However the page is 404ing, and outputting $wp->matched_query shows that it isn't selecting my rule, but one further down the list of rules (that was auto created by WP). It's actually matching the last rule in the $wp_rewrite->wp_rewrite_rules() array that starts with post-type.

Weird thing is that if I change the rule to:

add_rewrite_rule('post-type/(.+?)/(.+?)/(.+?)/?$', 'index.php?p=1234567&extravar=$matches[1]/$matches[2]&section=$matches[3]','top');

(i.e., purposely 404ing the page by going to an ID that doesn't exist, and just passing along the vars for reference) $wp->matched_query shows my regex, and the url is as it should be (index.php?p=1234567&extravar=parent-page/child-page&section=section-id)

What is the reason why my redirect URL is causing the rewrite engine to skip over my rule? Is something up with pagename?

Edit: Additional tests have revealed that going directly to http://www.domain.com/post-type/?pagename=parent-page/child-page&section=section-id works. so pagename is apparently valid.

Edit 2: More testing, changed the add_rewrite_rule call to check for post type as well...No Dice:

add_rewrite_rule('post-type/(.+?)/(.+?)/(.+?)/?$', 'index.php?post_type=post-type&pagename=$matches[1]/$matches[2]&section=$matches[3]','top');

I have also tried just using $matches[2], still not working:

add_rewrite_rule('post-type/(.+?)/(.+?)/(.+?)/?$', 'index.php?post_type=post-type&pagename=$matches[2]&section=$matches[3]','top');

The strange thing still is that building these URLs manually (as in filling in the query string values) actually loads the correct page. Don't know what the Rewrite API would make that any different.

  • Do 'post-type' is present on the permalink structure at WordPress permalink options? If so, maybe it's duplicating when you specify it on the custom rule. It may be looking for post-type/post-type/(.+?)/(.+?)/(.+?)/?$ Mar 29, 2016 at 15:03
  • That probably isn't it, since the regex works when I change the URL to purposely 404. It is capturing the URL correctly, but for some reason the original URL I redirect to is causing the rule to be skipped over.
    – Esaevian
    Mar 29, 2016 at 15:06
  • have you tried setting a post_type? I'll guess that pagename assumes the page post type if nothing is set.
    – Milo
    Mar 29, 2016 at 16:08
  • @Milo That's a good suggestion, but no dice. I edited the post with what I tried, and I''m still getting 404'd
    – Esaevian
    Mar 29, 2016 at 16:15

1 Answer 1


Okay, after a lot of playing around, I found this:

  1. The regex it was matching (while skipping over my rule) was practice-area/(.+?)(?:/([0-9]+))?/?$
    • This had a $matches array of 2: parent-page/child-page and a number that follows that, which is blank since there isn't one in the URL I was trying to build.
    • Also the URL it was redirecting to was index.php?post-type=$matches[1]&page=$matches[2]. I didn't realize that post-type could be a query var key. So that's what ultimately got me on the right path.
  2. So I decided to directly copy that, but instead of looking for a number I look for any character
    • I used post-type/(.+?)(?:/(.+))?/?$
    • This didn't work because I ended up with $matches[1] = parent-page and $matches[2] = child-page/section-id
  3. So, with a bit more tweaking, and messing with the laziness of the capture groups, I came up with post-type/(.+)/(.+?)/?$, which successfully superseded the regex in (1) above.
    • $matches[1] = parent-page/child-age and $matches[2] = section-id

The final rewrite rule is:

add_rewrite_rule('post-type/(.+)/(.+?)/?$', 'index.php?post-type=$matches[1]&section=$matches[2]','top');

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.