10 Ways to Customize RSS Feed in WordPress

The website's RSS feed (site feed) allows users to keep track of new content appearing on your blog. To do this, users subscribe to your blog through an RSS handler and receive new content from your blog in their RSS feed. Sometimes, for convenience or for other reasons, it is necessary to modify the post output in the feed. For example, adding custom post types to the feed, adding images to posts, and so on.

In this article, I will provide examples demonstrating how to customize feed output in WordPress. All the hooks provided below should be placed in the theme functions.php file or used to create a separate plugin. The plugin code will look like this:

<?php
/*
Plugin Name: My Site Feed Output
Description: Modifies the post output in the RSS feed.
*/

// Add code here...

Including Custom Post Types in the RSS Feed

Suppose we have created a new post type "book" using register_post_type() and would like posts of this type to appear in the RSS feed alongside regular posts. This can be done as follows:

add_filter( 'pre_get_posts', 'add_new_post_types_to_feed' );
function add_new_post_types_to_feed( $query ) {
	// Exit if this is not a feed request
	if( ! $query->is_feed || ! $query->is_main_query() ){
		return;
	}

	$query->set( 'post_type', array('post', 'book') );
}

If you also need to include pages, add "page" to the array: [ 'post', 'book', 'page' ].

Adding Post Thumbnail to the RSS Feed

We can hook into the_excerpt_rss, which triggers for the short post description in the feed, and add the post thumbnail to it:

add_filter( 'the_excerpt_rss', 'add_thumbnail_to_feed' );
add_filter( 'the_content_feed', 'add_thumbnail_to_feed' ); // this hook is usually not used, but it can be...
function add_thumbnail_to_feed( $content ){
	$img = get_the_post_thumbnail( null, [100, 80], [ 'align' => 'left', 'style' => 'margin-right:15px;' ] );
	$content =  $img . $content;

	return $content;
}

To obtain the thumbnail, we use the function get_the_post_thumbnail(), specifying the desired image size (100x80) in the second argument and the tag attribute in the third. Some RSS feed handlers remove inline CSS rules (style=''), so it's better to use align="left" when positioning the image to the left.

The specified size (100x80) is not the actual size but a proportionally resized copy of the original image to fit the specified dimensions. Sometimes it's better to create a special thumbnail image format for RSS feeds. In this case, you would need to register a new thumbnail size for your theme like this:

if ( function_exists( 'add_image_size' ) ) {
	// Thumbnail format for feeds
	add_image_size( 'feed', 100, 80 );
}

After adding this code to functions.php or a plugin, you can obtain the thumbnail by specifying the size feed:

$img = get_the_post_thumbnail( null, 'feed', [ 'align' => 'left' ] );

If you need to modify the displayed content rather than the short description, use the the_content_rss hook.

The appearance of the RSS feed depends on the feed reader program. Therefore, check how the feed looks in each specific case and do not try to make the output too non-standard.

Excluding Posts with a Specific Tag from the Feed

If you need to exclude posts with a specific tag (e.g., tag ID 451) from the RSS feed, use the following code:

add_filter( 'pre_get_posts', 'exclude_posts_from_feed_by_tag_id' );
function exclude_posts_from_feed_by_tag_id( $query ) {
	if( ! $query->is_feed || ! $query->is_main_query() ){
		return;
	}

	$query->set( 'tag__not_in', [ 451 ] );
}

To exclude posts with any of the specified tags, list the IDs of all the tags in the array:

[ 29, 31, 124 ]

Excluding Categories from the RSS Feed

To exclude unwanted categories from the feed, such as categories with the IDs 6 and 4, use the following code:

add_filter( 'pre_get_posts', 'exclude_cats_from_feed' );
function exclude_cats_from_feed( $query ){
	if( ! $query->is_feed || ! $query->is_main_query() ){
		return;
	}

	$query->set( 'cat', '-6,-4' );
}

Excluding a Category Tree from the RSS Feed

If you need to exclude posts from a category and all its subcategories, you can use the previous example and manually specify the category ID and all subcategory IDs. However, this is not convenient because if you add a new subcategory to this category, you will have to add the ID of the new subcategory to the code, otherwise the posts from it will appear in the feed, which is not desired.

To exclude posts from a category and automatically from all its subcategories, use the following code, where you only need to specify the ID of the parent category, and the IDs of all subcategories will be obtained automatically:

add_filter( 'pre_get_posts', 'exclude_cat_tree_from_feed' );
function exclude_cat_tree_from_feed( $query ){
	if( ! $query->is_feed || ! $query->is_main_query() ){
		return;
	}

	// ID of the category whose tree needs to be excluded
	$cat = 4;

	$subcats = get_categories( "child_of=$cat" );

	$subcat_string = '';
	foreach( $subcats as $subcat ){
		$subcat_string .= '-' . $subcat->cat_ID . ',';
	}
	$subcat_string .= "-$cat";

	$query->set( 'cat', $subcat_string );
}

Excluding Posts with a Custom Field from the Feed

Suppose we need to exclude random posts from the RSS feed: those not related to a tag, category, or other taxonomy. Then we can add a custom field exclude_from_feed with any value (e.g., 1) to such posts and use the code that excludes all posts with the custom field exclude_from_feed from the RSS feed:

add_filter( 'posts_where', 'exclude_special_posts_from_feed' );
function exclude_special_posts_from_feed( $where ){
	// Exit if this is not a feed request.
	if( ! is_feed() || ! is_main_query() ){
		return $where;
	}

	global $wpdb;

	$where .= " AND $wpdb->posts.ID NOT IN (
				SELECT distinct(post_id) from $wpdb->postmeta
				WHERE $wpdb->postmeta.meta_key = 'exclude_from_feed'
				) ";

	return $where;
}

Here, unlike the previous examples, we used the filter hook posts_where, which triggers every time a query is made.

Displaying Posts Only from Specified Categories in the Feed

In cases where you need to display posts from only one category in the feed, you can use the following code:

add_filter( 'pre_get_posts', 'my_categories_for_feed' );
function my_categories_for_feed( $query ){
	if( ! $query->is_feed || ! $query->is_main_query() ){
		return;
	}

	$query->set( 'category_name', 'life' );
}

To display posts from multiple categories, specify their names (slugs) separated by commas:

$query->set( 'category_name', 'life,love' );

You can also specify category IDs separated by commas:

$query->set( 'cat', '2,6,17,38' );

Adding a Source Link at the End of Each Post in the RSS Feed

There may be several reasons why you need to add a specific string to the end of each post in the RSS feed. For example, you may want to include your site's copyright for the posts in the feed by adding the following string to the end of each post: "Source: Site Name (site link)":

add_filter( 'the_excerpt_rss', 'add_text_to_the_feed_end' );
function add_text_to_the_feed_end( $content ){
	$content .= '
	<p>
		Source: <a href="'. get_bloginfo('url') .'">'. get_bloginfo('name') .'</a>.
	</p>
	';

	return $content;
}

In some cases, it's better to specify the link to the site as text because HTML tags can be stripped by RSS feed handlers, and if you specify a link with an anchor, the user may only see the anchor without the actual link.

Related Posts by Tags at the End of Each Post in the RSS Feed

Expanding on the idea of additional content for posts in the RSS feed, you can, for example, add links to posts with the same tag at the end of each post. That is, if a post has the tag "life," other posts with the tag "life" will be added to the end of this post in the feed:

add_filter( 'the_excerpt_rss', 'related_tag_posts_to_feed_end' );
add_filter( 'the_content_feed', 'related_tag_posts_to_feed_end' );
function related_tag_posts_to_feed_end( $content ){
	global $post;

	// Return from cache the second time
	$cache_key = __FUNCTION__ . $post->ID;
	if( $cache = wp_cache_get( $cache_key ) ){
		return $content . $cache;
	}

	// Get tags
	$tag_ids = wp_get_post_tags( $post->ID, [ 'fields' => 'ids' ] );

	// If there are tags, get related posts
	if( $tag_ids ){
		$out = '';
		$args = array(
			'posts_per_page' => 3,
			'tag__in'        => $tag_ids,
			'post__not_in'   => [ $post->ID ],
		);
		$posts = get_posts( $args );
		if( $posts ){
			foreach( $posts as $p ){
				$out .= '<li>'. get_permalink( $p->ID ) . ' </li>';
			}
			$out = make_clickable( $out );
			$content .= '<p>Read also: <ul>'. $out .'</ul></p>';
		}
	}

	// Cache the result
	wp_cache_set( $cache_key, $out );

	return $content;
}

Adjusting the Number of Posts Displayed in the RSS Feed

Usually, you can set the number of posts displayed in the RSS feed in the settings: Settings > Reading > Syndication feeds show the most recent. However, if you need to change the number of posts through a plugin or in any other cases where standard settings are not suitable, you can use the following code:

add_filter( 'pre_get_posts', 'how_many_posts_display_in_feed' );
function how_many_posts_display_in_feed( $query ) {
	if( ! $query->is_feed || ! $query->is_main_query() ){
		return;
	}

	// This option does not work
	// $query->set( 'posts_per_page', 11 );

	// Number of posts to display
	$n = 7;
	add_filter( 'post_limits', function( $n ){ return "LIMIT $n"; } );
}

Here, we used the post_limits filter, which allows us to intercept the SQL query and change the number of rows retrieved in the query (LIMIT 7) in our case, the number of posts.

Delaying Post Publication in the RSS Feed

To make a published post appear in the RSS feed not immediately, but with a delay of a specified time, can be useful for various reasons. For example, I often find some minor errors right after publication. Or the feed is actively monitored, and you can delay publication for a day or two so that the published content is first seen by search engines and then by everyone else.

## Delay before publishing a post in the RSS feed
add_filter( 'pre_get_posts', 'delay_post_to_feed' );
function delay_post_to_feed( $query ){
	global $wpdb;

	if( $query->is_feed && $query->is_main_query() && ! $GLOBALS['wp_query']->is_comment_feed ){
		$query->set( 'date_query', [
			'before' => '12 hour ago', // get posts published 12 hours ago
		]);
	}
}

Removing all feed from rewrite rules (permalinks), except the feed on the main page

All feed links will stop working and will return a 404 error, except for the feed on the main page:

## removes all feed rewrite rules, except the feed on the main page
is_admin() && add_filter( 'rewrite_rules_array', 'delete_all_feed_rewrites_rules' );
function delete_all_feed_rewrites_rules( $rules ){

	foreach( $rules as $rule => $val ){
		if(
			strpos($rule, 'feed/(')
			|| ( strpos($rule, '/(feed') && 0 !== strpos($rule, 'feed/(feed') )
		){
			unset( $rules[ $rule ] );
		}
	}

	return $rules;
}

After installing the code, you need to reset the rewrite rules in the permalink settings...

Along with this code, you also need to remove feed links from wp_head, perhaps for the main page such links should be left:

add_action( 'wp', function(){
	if( is_front_page() ){
		return;
	}

	remove_action( 'wp_head', 'feed_links_extra', 3 );
	remove_action( 'wp_head', 'feed_links', 2 );
	remove_action( 'wp_head', 'rsd_link' );

} );

Hooks used in the examples:

  1. pre_get_posts - allows to modify the query before the actual database query is made;

  2. the_excerpt_rss - filters the content of the excerpt for syndicated feeds;

  3. the_content_feed - filters the content of the post after it has been retrieved from the database and processed by the the_content filter;

  4. posts_where - modifies the WHERE part of the SQL query;

  5. post_limits - modifies the LIMIT part of the SQL query before posts are retrieved from the database.