Dynamically exclude items from Yoast SEO sitemap
If you are like me, you love the Yoast SEO plugin; one of the, if not the, most popular plugin for WordPress.
One of its features is to exclude pages from the sitemap. You can do this by going to the ‘Yoast meta box’ a post and exclude it from the search results.
Personally I like a more dynamic approach to the matter, I like to easily see which items are excluded, so I use some functions to make it more ‘fluid’.
Note: The plugin Advanced Custom Fields is necessary for this, but which website doesn’t run this plugin nowadays. Almost a must-have, like Yoast SEO.
If you don’t have ACF you can still use it, but then you need to use WordPress’ custom fields and change the get_field functions to get_post_meta.
The steps are:
- Create an option to ‘mark’ an item as excluded (with ACF)
- Create a function within a transient to retrieve these items
- Delete transient after an edit
- Return excluded items
1. Create an option to ‘mark’ an item as excluded
Create a new group in ACF, as explained here.
Add a field with the Field Name “beee_exclude_sitemap”as explained here.
Show it on the post types which you want excluded.
2. Create a function to retrieve these items
Add this to your functions.php.
/**
* Get all excluded items for sitemap
*
* @return array|mixed
*/
function beee_get_sitemap_excludes_transient() {
// get transient
$output = get_transient( 'beee_exclude_sitemap' );
// if transient returns false, create it
if ( false == $output ) {
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
$exclude_args = array(
'post_type' => [ 'page', 'your-post-type' ],
'posts_per_page' => -1, // all items of course
'suppress_filters' => false,
'meta_query' => array(
array(
'key' => 'beee_exclude_sitemap',
'value' => '1',
'compare' => '=',
),
),
);
$excluded_items = get_posts( $exclude_args );
if ( count( $excluded_items ) > 0 ) {
// if there are items and create an empty array for any output
$output = [];
foreach( $excluded_items as $item ) {
// add post id to array
$output[] = $item->ID;
}
// set transient with a 24-hour expiration
set_transient( 'beee_exclude_sitemap', $output, DAY_IN_SECONDS );
}
}
return $output;
}
On line 9 we retrieve the stored transient.
If it returns false (line 12) we create it.
Lines 15-27 is the actual query to retrieve the posts which have our custom ‘exclude’ field.
Line 29 checks if there is at least 1 result.
If so an empty array is created on line 31.
We then loop through the array (lines 32-35) and add the post ID to a new array (line 34), which we created on line 31.
We store it in a transient (line 38).
And finally we return the value (the excluded items as a string) on line 42.
3. Delete transient after an edit
Also add this to functions.php.
/**
* Delete exclude sitemap transient
*/
function beee_sitemap_page_ids( $post_id ) {
if ( 'page' == get_post_type( $post_id ) ) {
delete_transient( 'beee_exclude_sitemap' );
}
}
add_action( 'save_post', 'beee_sitemap_page_ids' );
After a post is saved, we check if it the post that was saved, is a page. If so, we delete the transient with page IDs because in case the settings changed, we need ‘a new transient’.
We then check if the post that just edited is a page.
If so, we delete the transient with page IDs because in case the settings changed, we need ‘a new transient’.
4. Return excluded items
Then the function where this transient is actually retrieved. This is a filter which hooks into WordPress SEO.
Also add this to functions.php.
/**
* Return excluded items from Yoast sitemap
*
* @return array|mixed
*/
function beee_exclude_pages_sitemap() {
return beee_get_sitemap_excludes_transient();
}
add_filter( 'wpseo_exclude_from_sitemap_by_post_ids', 'beee_exclude_pages_sitemap' );
And there you have it. Yes, I know it could be maybe written a bit shorter, but I wanted to optimise it as much as possible. The benefit of doing it this way is that the query to retrieve all excluded items is not run after each save action.
TLDR
Yoast has a filter which you can use to filter which posts/pages will appear in the sitemap. This filter is `wpseo_exclude_from_sitemap_by_post_ids`, see this link.