Let’s say you plan to get into podcasting. You have the recording equipment, an interesting topic, and a good voice that other people want to hear. You seem well on your way to filling earbuds everywhere.
Then there’s the issue of hosting your podcast. iTunes requires an RSS feed so it can distribute your episodes to subscribers when they’re available, but how do you do that? The good news is that there are plenty of ways to host a podcast. For example, you could use a hosting service that provides you storage and distribution in one tidy place, usually for a fee. If you use WordPress and have looked into podcasting, you may have seen all the powerful podcasting plugins that are available there as well.
We’re going to skip over those options in this post and see how we can host our own podcast using nothing more than a WordPress site and two additional files. Specifically, we’re going to:
- Set up a new plugin
- Register a custom RSS feed
- Make a custom post type for our podcast
- Assign custom fields to our custom post type
- Create the template for our RSS feed
The goal is not to overthink this too much. If you already have a WordPress site and all you need is a feed to submit to iTunes, then that’s exactly what we’re going to do. Let’s rock ‘n’ roll.
Setting Up the Plugin
Creating a plugin is a nice alternative to writing code in your theme’s `functions.php` file. In addition to keeping your theme’s functions clean and clutter-free, it keeps the podcast feed functionality in your `/wp-content/plugins/` directory which stays with you even if you change themes.
Create the Plugin Directory
First things first. Let’s create a new folder called `my-awesome-podcast` in our `/wp-content/plugins/` directory.
Create the Plugin
Now let’s make a new PHP file in the folder we just created and call it `my-awesome-podcast.php`. This will be the primary file that registers our plugin and where we write the functionality for our podcast.
With that file in place, we will add the following code comments that tell WordPress the name, description and version of the plugin. There are other bits of information we can add, but this will do.
/**
Plugin Name: My Awesome Podcast
Description: A really simple RSS feed and custom post type for a podcast
Version: 1.0
**/
// This is where we will start writing our plugin
Register the RSS Feed
Next up, we need to register a new RSS feed. Thankfully, the WordPress API has a handy add_feed() function that we can hook into to make this relatively easy.
add_action('init', 'podcast_rss');
function podcast_rss(){
add_feed('my-awesome-podcast', 'my_podcast_rss');
}
What’s happening here is we’ve defined a new function called podcast_rss()
and are extending the WordPress add_feed()
function to create a new RSS feed called “my_podcast_rss” that will live at `/feed/my-awesome-podcast` on our site.
Note: You can change “my-awesome-podcast” here to anything you want. This is the URL slug of your feed, so it could be the title of your podcast or whatever else you fancy.
Register a Podcast Custom Post Type
Once we have our feed established, we’ll need to set up a new post type that we can use to publish the posts for our episodes. So instead of a “Post” or “Page” that are default to WordPress, we’ll create a brand new one called “Podcast”. The WordPress Codex does a good job of explaining how to create custom post types, so there’s no need to rehash that here. There are even a handful of plugins that will do this, if that’s how you prefer to roll. In any case, let’s assume we’re doing it without the help of a plugin and make the Podcast custom post type here in our file.
function custom_post_type() {
$labels = array(
'name' => _x( 'Podcasts', 'Podcast General Name', 'text_domain' ),
'singular_name' => _x( 'Podcast', 'Course Singular Name', 'text_domain' ),
'menu_name' => __( 'Podcasts', 'text_domain' ),
'parent_item_colon' => __( 'Parent Podcast:', 'text_domain' ),
'all_items' => __( 'All Podcasts', 'text_domain' ),
'view_item' => __( 'View Podcast', 'text_domain' ),
'add_new_item' => __( 'Add New Podcast', 'text_domain' ),
'add_new' => __( 'Add New', 'text_domain' ),
'edit_item' => __( 'Edit Podcast', 'text_domain' ),
'update_item' => __( 'Update Podcast', 'text_domain' ),
'search_items' => __( 'Search Podcasts', 'text_domain' ),
'not_found' => __( 'Not found', 'text_domain' ),
'not_found_in_trash' => __( 'Not found in Trash', 'text_domain' ),
);
$args = array(
'label' => __( 'podcasts', 'text_domain' ),
'description' => __( 'Podcast Description', 'text_domain' ),
'labels' => $labels,
'supports' => array( 'title', 'editor', 'thumbnail' ),
'taxonomies' => array( 'category', 'post_tag' ),
'hierarchical' => false,
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'show_in_nav_menus' => true,
'show_in_admin_bar' => true,
'menu_position' => 5,
'menu_icon' => 'dashicons-format-audio',
'can_export' => true,
'has_archive' => true,
'exclude_from_search' => false,
'publicly_queryable' => true,
'capability_type' => 'page'
);
register_post_type( 'podcasts', $args );
}
add_action( 'init', 'custom_post_type', 0 );
This is a lot of code, but it’s just configuration to register a new post type called Podcasts. It defines the various labels for it in the WordPress dashboard, and tells WordPress how to handle it. Again, the WordPress Codex explains these things much more exhaustively, if you’re interested.
At this point in time, we can head into the Plugins section of our WordPress admin and see My Awesome Podcast listed as an available plugin. Go ahead and activate it.

Create Custom Fields for Podcast Posts
WordPress posts do a lot for us right out of the box, but sometimes we need additional fields that allow us to publish specific content. In this case, iTunes has a very specific format for what kind of information needs to be available in our RSS feed.
There is a way to add custom fields to our custom post type with extra development, but I actually prefer using the Advanced Custom Fields plugin for this sort of thing. It allows us to have a nice clean custom UI for each field we add, rather than the generic key/value pair UI you get with default custom fields.
Using Advanced Custom Fields, let’s set up a new field group called Podcasts, assign them to the Podcast post type and create the following fields:

- podcast_file: Use the “File” field type so we can upload our podcast file directly to the post. Set the field to Return Value to “File ID” because this will help us auto-detect the file size later.
- podcast_duration: This is a simple text field that we’ll use to publish the length of our episode in HH:MM:SS format.
Note: While I used the “File” field type in this example, you could theoretically use a text field if you plan on hosting your podcast files somewhere other than your server (say Amazon S3 or something like that), and paste the URL directly in there. However, we won’t be able to auto-detect the file size this way, though we could use another text field for that purpose.
Create the RSS Template
We’ve done a lot so far. The last thing to do is create the template that will be used to display the data for our podcast posts.
Apple has outlined the specs for an acceptable podcast RSS feed and it’s very specific. Using that outline as a guide, let’s create one more file in our plugin folder and call it `podcast-rss-template.php`. The name says it all, right?
The nice thing is that our RSS template is technically no different from any other WordPress template. We will query our posts, create a loop, and plug our data in where it needs to go.
/**
Template Name: Podcast RSS
**/
// Query the Podcast Custom Post Type and fetch the latest 100 posts
$args = array( 'post_type' => 'podcasts', 'posts_per_page' => 100 );
$loop = new WP_Query( $args );
/**
* Get the current URL taking into account HTTPS and Port
* @link https://css-tricks.com/snippets/php/get-current-page-url/
* @version Refactored by @AlexParraSilva
*/
function getCurrentUrl() {
$url = isset( $_SERVER['HTTPS'] ) && 'on' === $_SERVER['HTTPS'] ? 'https' : 'http';
$url .= '://' . $_SERVER['SERVER_NAME'];
$url .= in_array( $_SERVER['SERVER_PORT'], array('80', '443') ) ? '' : ':' . $_SERVER['SERVER_PORT'];
$url .= $_SERVER['REQUEST_URI'];
return $url;
}
// Output the XML header
header('Content-Type: '.feed_content_type('rss-http').'; charset='.get_option('blog_charset'), true);
echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>';
?>
<?php // Start the iTunes RSS Feed: https://www.apple.com/itunes/podcasts/specs.html ?>
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
<?php
// The information for the podcast channel
// Mostly using get_bloginfo() here, but these can be custom tailored, as needed
?>
<channel>
<title><?php echo get_bloginfo('name'); ?></title>
<link><?php echo get_bloginfo('url'); ?></link>
<language><?php echo get_bloginfo ( 'language' ); ?></language>
<copyright><?php echo date('Y'); ?> <?php echo get_bloginfo('name'); ?></copyright>
<itunes:author><?php echo get_bloginfo('name'); ?></itunes:author>
<itunes:summary><?php echo get_bloginfo('description'); ?></itunes:summary>
<description><?php echo get_bloginfo('url'); ?></description>
<itunes:owner>
<itunes:name><?php echo get_bloginfo('name'); ?></itunes:name>
<itunes:email><?php echo get_bloginfo('admin_email'); ?></itunes:email>
</itunes:owner>
<?php // Change to your own image. Must be at least 1400 x 1400: https://www.apple.com/itunes/podcasts/creatorfaq.html
<itunes:image href="http://your-site.com/path/to/podcast/image.png" />
<itunes:category text="Technology">
<itunes:category text="Tech News"/>
</itunes:category>
<itunes:explicit>yes</itunes:explicit>
<?php // Start the loop for Podcast posts
while ( $loop->have_posts() ) : $loop->the_post(); ?>
<item>
<title><?php the_title_rss(); ?></title>
<itunes:author><?php echo get_bloginfo('name'); ?></itunes:author>
<itunes:summary><?php echo get_the_excerpt(); ?></itunes:summary>
<?php // Retrieve just the URL of the Featured Image: http://codex.wordpress.org/Function_Reference/wp_get_attachment_image_src
if (has_post_thumbnail( $post->ID ) ): ?>
<?php $image = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'full' ); ?>
<itunes:image href="<?php echo $image[0]; ?>" />
<?php endif; ?>
<?php // Get the file field URL, filesize and date format
$attachment_id = get_field('podcast_file');
$fileurl = wp_get_attachment_url( $attachment_id );
$filesize = filesize( get_attached_file( $attachment_id ) );
$dateformatstring = _x( 'D, d M Y H:i:s O', 'Date formating for iTunes feed.' );
?>
<enclosure url="<?php echo $fileurl; ?>" length="<?php echo $filesize; ?>" type="audio/mpeg" />
<guid><?php echo $fileurl; ?></guid>
<guid><?php the_field('podcast_file'); ?></guid>
<pubDate><?php echo date($dateformatstring, $show_date); ?></pubDate>
<itunes:duration><?php the_field('podcast_duration'); ?></itunes:duration>
</item>
<?php endwhile; ?>
</channel>
</rss>
Note: the code above assumes you’re uploading the MP3 file to your own site/server. When that’s the case, it can get the file size in bytes, which iTunes requires as part of the RSS feed, which is super handy. However, if you’re hosting the MP3 file elsewhere (can be smart, most web hosts aren’t built for serving large assets), this won’t work. Instead, add an additional field with ACF for the byte size and output it where we’re outputting $filesize
above.
Now let’s call this RSS template in our `my-awesome.podcast.php` file by adding the following:
function my_podcast_rss(){
require_once( dirname( __FILE__ ) . '/podcast-rss-template.php' );
}
Putting it All Together
Let’s recap how everything fits together.
The Files
In short, we now have a new folder in our plugin directory called `my-awesome-podcast`.
WordPress Root Directory
└── wp-content
└── plugins
└── my-awesome-podcast
├── my-awesome-podcast.php
└── podcast-rss-template.php
- my-awesome.podcast.php: This registers our plugin, creates a custom RSS feed, defines a new custom post type and calls our RSS template.
- podcast-rss-template.php: This contains the template for our RSS feed, providing the information from our Podcasts posts in a format that iTunes can read and use to deliver our content to subscribers.
Publishing a Podcast
Go ahead and publish a new Podcast post. The option will be in your WordPress menu under Podcasts.

- Give the podcast a title
- Upload the podcast episode file
- Specify the episode length (in HH:MM:SS format)
- (if not hosting locally) Specify the episode file size (in bytes)
- Provide a summary of the episode in the content area
Awesome, we just published your first podcast episode!
Submit the Podcast to iTunes
Finally, go visit the URL of the feed at: `[your-site]/feed/[slug]`, where `[slug]` is what we defined in the add_feed()
function back in the second step. It wouldn’t hurt to validate the feed just to make sure it’s healthy and happy. This is the URL you will submit to iTunes, which you can do from the iTunes app under iTunes Store > Podcasts > Quick Links > Submit a Podcast.
If you’re getting a Page Not Found (404), try flushing your permalinks. This can be done by heading to Settings > Permalinks from your WordPress dashboard and doing nothing more than clicking the Save Settings button. Strange, but true.
Go Forth and Podcast
There you go! A simple, but effective way to host your podcast using resources you probably already have lying around. Sure, there may be more powerful or even more elegant solutions, but this gives us exactly what we need with nothing more and nothing less.
Update: A huge shout-out to David Scanu. He wrote in with some updates to that correct a few RSS validation issues, which are now reflected in the article. Thanks a ton!
One quick thought I had to make the experience a little bit simpler: let the software grab the podcast’s time. There’s a couple ways to do that but this simple class seems to do the trick without relying on a giant code base (like the massive getId3() library). However, if use podcast using an aac (mp4) or some other format, look like getId3 is among the best options. (If you tag the files correctly, you could even extract a bit more from them as well but beyond the scope of this article.)
Nice! I hadn’t seen that class before–could definitely be useful, especially if ACF isn’t an option for some folks.
Thanks for a great article, Geoff! For me this is perfectly timed because I’m about to build podcast functionality into a current project, and the podcast plugins out there weren’t quite fitting my needs.
I’m also going to have a single post template so folks can visit the site and listen in the browser. How would you get the built-in WP audio player to work with
<?php the_field('podcast_file'); ?>
?Good question! I imagine you could use the
do_shortcode()
function to inject the audio player in the template, then echo the$fileurl
variable we defined in the RSS template as the source file for the audio player.Thanks, Geoff. I thought of
do_shortcode()
, but wasn’t sure if there was a better way.It’s odd that there isn’t a simple way to grab the duration automatically since the built-in audio player can. I looked at
wp_read_audio_metadata
but don’t see anything about getting duration. I’d like to not make multiple contributors have to enter in the durations every time, both for convenience and to eliminate errors.Great article! This is one of the best posts I’ve read on creating a simple plugin. It’s functional, feature full, and solves a problem a lot of users have.
Just a note: Under the section “Create Custom Fields for Podcast Posts” the link to the Advanced Custom Fields plugin doesn’t seem to be working.
Thanks man! I’m stoked you find this useful. :)
Nice catch on the URL. For the record, this is the correct link: http://www.advancedcustomfields.com/
Nice article but for publishing podcasts with WordPress I recommend the Podlove Publisher:
http://podlove.org/podlove-podcast-publisher/
This is pretty awesome. We use a very similar custom solution for a Podcast plugin on http://realwpsite.com
Thank you for this great useful article, Geoff! I was planning to launch my own podcast on my WordPress personal website and just as I saw this great article, I’ll do my best to launch it as soon as possible.
Jeff