By doing a simple:

add_feed( 'schedule', 'my_schedule_feed' );

I have created a feed at /feed/schedule/... easy.

But when I go to /schedule/ (which is a page) it is now showing the feed!

Looking at the rewrite rules I see this:

[feed/(feed|rdf|rss|rss2|atom|schedule)/?$] => index.php?&feed=$matches[1]
[(feed|rdf|rss|rss2|atom|schedule)/?$] => index.php?&feed=$matches[1]

This seems really odd. Why would WordPress overwrite existing page rules with the feed, instead of just using only the /feed/ base as expected?

Update: I have tried this as suggested by this answer, and flushing the permalinks, but while it shows up in the rewrites, it does not show the feed at /feed/schedule/

function add_feed_rewrites( $wp_rewrite ) {
    $feednames = array( 'schedule', 'calendar' );
    $feeds = implode( '|', $feednames);
    $feed_rule = array(
        'feed/(' . $feeds . ')/?$' => 'index.php?&feed=$matches[1]'
    $wp_rewrite->rules = array_merge( $wp_rewrite->rules, $feed_rule );    
add_filter( 'generate_rewrite_rules', 'add_feed_rewrites' );

add_action( 'do_feed_schedule', 'my_schedule', 10, 2 );
add_action( 'do_feed_calendar', 'my_calendar', 10, 2 );

Any ideas on how to get this working without the initial conflict?

1 Answer 1


If you don't want to use add_feed(), then you can use add_rewrite_rule():

add_action( 'init', function () {
} );

add_action( 'do_feed_schedule', function () {
    header( 'Content-Type: text/plain' ); // dummy type just for testing
    echo 'Yay, it works!';
} );

But regarding the original question about add_feed()..

Why would WordPress overwrite existing page rules with the feed, instead of just using only the /feed/ base as expected?

So that example.com/schedule would serve your custom feed, just like standard feed URLs such as example.com/feed. But WordPress does create the (proper) rules having the feed/ base for custom feed URLs like example.com/feed/schedule.

Therefore you shouldn't use schedule as the slug of your Page, or if you have/need to, then you can alter the feed rewrite so that the example.com/schedule will serve the Page instead of your feed:

  • If you know the exact rule/RegEx:

    add_filter( 'rewrite_rules_array', function ( $rules ) {
        $rules2 = [];
        foreach ( $rules as $regex => $query ) {
            if ( '(feed|rdf|rss|rss2|atom|schedule)/?$' === $regex ) { // it has 'schedule'
                $rules2['(feed|rdf|rss|rss2|atom)/?$'] = $query;       // so let's remove it
            } else {
                $rules2[ $regex ] = $query;
        return $rules2;
    } );
  • Otherwise, try something dynamic like this:

    add_filter( 'rewrite_rules_array', function ( $rules ) {
        global $wp_rewrite;
        $feeds = $wp_rewrite->feeds;         // original feeds
        $pages = [ 'schedule', 'calendar' ]; // page slugs list
        $feeds2 = array_diff( $feeds, $pages );
        $rules2 = [];
        $feeds = implode( '|', $feeds );
        $feeds2 = implode( '|', $feeds2 );
        foreach ( $rules as $regex => $query ) {
            if ( '(' . $feeds . ')/?$' === $regex ) {
                $rules2[ '(' . $feeds2 . ')/?$' ] = $query;
            } else {
                $rules2[ $regex ] = $query;
        return $rules2;
    } );

Note that I use the foreach so that the rule stays in its original position. And another possible option is that just move the rule to a position after the Page rewrite rules. But I haven't yet tried that and the above code should be enough as a workaround..

And if I'm not mistaken, here's the code which WordPress use to generate the rule that's being altered in the above function:

// Create query for /(feed|atom|rss|rss2|rdf) (see comment near creation of $feedregex).
$feedmatch2 = $match . $feedregex2;
$feedquery2 = $feedindex . '?' . $query . '&feed=' . $this->preg_index( $num_toks + 1 );

See the source on Trac.

  • 1
    thank you for the detailed response!!! I'll definitely get it working now, rewrites are one thing that really have me scratching my head. I guess I was surprised I couldn't find this mentioned anywhere, everywhere just seems to say "your feed will be available via /feed/" which is not very informative... This is for a plugin so I don't have control over what pages may already exist, and schedule is very likely to exist already in the context I'm working on, so I simply can't have it overwriting pages at all.
    – majick
    Oct 31, 2019 at 5:01
  • That's understandable. :) And actually, there's another option when using add_feed() if you don't want to alter the rule generated by WordPress: add_rewrite_rule( '^(schedule|calendar)/?$', 'index.php?pagename=$matches[1]', 'top' ) - but you may need to add other rules for that Pages, depending on things like whether paginated requests/URLs (e.g. example.com/schedule/2) should be enabled.
    – Sally CJ
    Oct 31, 2019 at 13:54

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.