RT Cunningham


A Simple, Custom XML Sitemap Generator for WordPress

sitemap The good WordPress plugins for XML sitemaps don’t produce static sitemap files. This is the PHP script I use, which I consider better for my purposes. It creates a static sitemap file that doesn’t include any of the taxonomy archive pages (categories, tags, date-based, etc.). My website doesn’t use any of those pages and even if it did, only the regular posts and pages are important.

My script only supports permalinks set with “/%postname%/”. You’ll have to change it support a different format.

The Sitemap Script through WordPress

There are two ways to run this script. One is by using WordPress functions and the other is by using Cron. I prefer Cron and I run it once an hour. I doubt Google retrieves the sitemap more than once a day but I don’t know what part of the day that is.

These are the WordPress functions to use if you want the sitemap generated every time you add, edit or remove a post (you obviously have to edit the “include” line for both):

function sitemap_update($new_status, $old_status, $post) {
  if ($new_status != $old_status) {
    include '/path-to-script/script.php';
add_action('transition_post_status', 'sitemap_update', 10, 3);
function sitemap_edit($post_ID, $post) {
  if ($post->post_status == 'publish') {
    include '/path-to-script/script.php';
add_action('edit_post', 'sitemap_edit', 10, 2);

This is the script to go with them:

$today = date( 'Y-m-d' );
$site_path = get_home_path();
$sitemap_filename = $site_path . '/sitemap.xml';
$site_url = home_url( '/' );
$results = get_posts( array('numberposts' => -1, 'orderby' => 'modified', 'post_type' => array( 'post','page' ) ) );
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>' . "n" .
           '<urlset xmlns="">' . "n" .
           "t<url>n" .
           "tt<loc>$site_url</loc>n" .
           "tt<lastmod>$today</lastmod>n" .
           "tt<changefreq>monthly</changefreq>n" .
foreach ($results as $post) {
  $filename = $post->post_name;
  if ( strstr( $filename, '?' ) ) continue; // an odd quirk I discovered
  $a = explode( ' ', $post->post_modified );
  $postdate = $a[0];
  $sitemap .= "t" . '<url>' . "n" .
              "tt<loc>$site_url$filename/</loc>n" .
              "tt<lastmod>$postdate</lastmod>n" .
              "tt<changefreq>daily</changefreq>n" .
$sitemap .= '</urlset>' . "n";
file_put_contents( $sitemap_filename );

The Sitemap Script through Cron

I put all the PHP scripts I run from Cron in the directory above the WordPress root directory. That directory isn’t publicly accessible. Two changes must be made to the above script to run it from Cron. The first change is to replace the first two lines (not counting the PHP opening tag) with:

date_default_timezone_set( 'Asia/Manila' ); // your timezone not mine
$today = date( 'Y-m-d' );
$site_path = __DIR__ . '/document-root';    // change "document-root" to the WordPress root directory name
include $site_path . '/wp-load.php';

The second change is to insert these lines at the bottom (before the PHP closing tag):

chmod ( $sitemap_filename, 0644 );
chgrp( 'www-data' );
chown( 'www-data' );

According to, you can have up to 50,000 URLs in a single sitemap file. Since I only put posts and pages (no taxonomies) in my sitemap file, I’ll never reach 50,000 URLs. Not in this lifetime, anyway.

Share: Facebook | Twitter

By RT Cunningham
August 16, 2016
Web Development