Skip to content

How to Build the Perfect WordPress Sitemap

I was recently asked how I built the sitemap on Binary Moon so I thought I would share the code. The sitemap layout is actually one of the many custom page templates available in my WordPress theme, Elemental – however the code is very loosly based upon that in the original SRG Clean Archives plugin (before it was taken over by Geek With a Laptop).

Sitemaps are a single page that allows you to view every single post on a site. Perfect for Google to spider all of your content, and for users to quickly find old posts that they are looking for.

When making the sitemap I wanted to list all of the posts, and also to link to the monthly archives for each post – that way I would get maximum exposure for my sitemap – and it has a page rank of 6 (at the time of writing) so I must be doing something right.

The process is relatively simple:

  1. Grab all the months and years from the database
  2. Loop through the list of dates and grab the posts from each month, displaying them as you go

Easy right? The code, that you would add to your functions.php – would look something like the function below:

function bm_displayArchives() {
	global $month, $wpdb, $wp_version;

	// a mysql query to get the list of distinct years and months that posts have been created
	$sql = 'SELECT
			DISTINCT YEAR(post_date) AS year,
			MONTH(post_date) AS month,
			count(ID) as posts
		FROM ' . $wpdb->posts . '
		WHERE post_status="publish"
			AND post_type="post"
			AND post_password=""
		GROUP BY YEAR(post_date),
		ORDER BY post_date DESC';

	// use get_results to do a query directly on the database		
	$archiveSummary = $wpdb->get_results($sql);

	// if there are any posts
	if ($archiveSummary) {
		// loop through the posts
		foreach ($archiveSummary as $date) {
			// reset the query variable
			unset ($bmWp);
			// create a new query variable for the current month and year combination
			$bmWp = new WP_Query('year=' . $date->year . '&monthnum=' . zeroise($date->month, 2) . '&posts_per_page=-1');

			// if there are any posts for that month display them
			if ($bmWp->have_posts()) {
				// display the archives heading
				$url = get_month_link($date->year, $date->month);
				$text = $month[zeroise($date->month, 2)] . ' ' . $date->year;
				echo get_archives_link($url, $text, '', '<h3>', '</h3>'); 
				echo '<ul class="postspermonth">';
				// display an unordered list of posts for the current month
				while ($bmWp->have_posts()) {
					echo '<li><a href="' . get_permalink($bmWp->post) . '" title="' . wp_specialchars($text, 1) . '">' . wptexturize($bmWp->post->post_title) . '</a></li>';
				echo '</ul>';			

Note that I am using the wp_query object (as detailed in my post “10 query_posts tips you probably don’t know“) throughout this example to stop the main query_posts instance from being ruined

To use the function above you would then need to create a custom page template (as detailed in the custom page template tutorial here). You can then call the function from within the new template.

Ben View All

Ben is a lifelong Nintendo fan who also likes to build websites, and develop games. He also buys way too much Lego.

23 thoughts on “How to Build the Perfect WordPress Sitemap Leave a comment

  1. I use the Google Sitemap Generator plugin. Can this code still be implemented, or would that be redundant?

    • These are for two different things. The Sitemap Generator plugin is used to generate xml sitemaps whereas the code I used it for displaying html sitemap pages on your website.

      BTW I changed your name to something resembling your real name 🙂

  2. This is terrific. I also need to generate a Site Map where the Posts are listed by Category rather than date. Basically, I want to build a site map that reads like a Table of Contents for Posts … where posts are listed alphabetically by title within their category, and reflecting the category hierarchy. Any ideas? Thanks!

  3. This article feels a bit half-finished, unless I’ve missed something. Once the code is added to functions.php, how do you display the sitemap on a page?

    • Hi Joe – thanks for the feeback – I have added some extra notes to the bottom of the post to try to clarify what to do with the function.

  4. Hi Ben,

    Thanks for sharing this, I think your sitemap looks great. I’ve added the function to my functions.php and called it in my page temple. Unfortunately it’s not outputting anything at all!

    This is how I called the function…

    Is there any reason that your code isn’t working for me?


  5. great tutorial and got it all working quite easily, however, unlike your sitemap on Binary Moon, this only outputs posts and not pages. Anyway to amend so that it generates page links in the sitemap?

    I’m only just getting my head around WordPress but know a bit of PHP and SQL and have tried altering your SQL statement:
    AND post_type=”post”


    AND post_type=”page”

    however it still generates a map based on pages. Is it something to do with the_post() function?

    thanks again though!

  6. Hi Ben. Hopefully everything is good with you. I have paste the code given to my function.php but some of the script appear on top of my page header. Can you tell what I did wrong?

  7. Hello Ben,
    I have an HTML based sitemap but it seems like Google don’t like them much. May .xml based sitemap would be more appropriate. Any idea? Is there anyway I can create .xml sitemap without any PLUGIN? which won’t need manual update? I do blog nearly everyday. So, manual .xml sitemap gonna be a real boring thing to do. Please help. Thanks.

    • My tutorial was for a public sitemap. If you want an automated system then the best thing to do is use a plugin – there’s a couple of good ones out there so just google around :).

  8. hello
    I tried your code and it works nicely but it sticks with the posts and I would like it to list only my custom posts (livres).
    So in the mysql query, i put AND post_type=’livres’
    but it keeps displaying regular posts instead of books !
    Should I modify more the query ?
    thank you

Leave a Reply

Your email address will not be published. Required fields are marked *