XML sitemaps are important for SEO, it’s like feeding a baby with a spoon full of food and Google’s the baby… They give their spiders a list of pages to crawl and it helps them understand your architecture and index content/URLs.
There are plugins out there such as Yoast SEO that can generate them automatically but there might be certain cases where you need to build a custom sitemap. For example, you might have hosting restrictions, specific URL requirements, or using a reverse proxy.
Table of Contents
This guide explains how to create a custom XML sitemap in WordPress particulary for a reverse proxy use case but can be adapted for other cases too…
Step 1: Intercept Sitemap Requests
Intercept requests for custom sitemap URLs (e.g., /custom-sitemap_index.xml
) and generate the required XML directly. Use the init
hook to catch the requests early.
add_action('init', function () {
if (isset($_SERVER['REQUEST_URI'])) {
$custom_sitemaps = [
'/custom-sitemap_index.xml' => 'index',
'/custom-post-sitemap.xml' => 'post',
'/custom-page-sitemap.xml' => 'page',
'/custom-category-sitemap.xml' => 'category',
'/custom-author-sitemap.xml' => 'author',
];
foreach ($custom_sitemaps as $path => $type) {
if (strpos($_SERVER['REQUEST_URI'], $path) === 0) {
if ($type === 'index') {
generate_custom_sitemap_index();
} else {
generate_custom_sitemap($type);
}
exit;
}
}
}
});
This ensures WordPress processes the correct XML before default templates or redirects interfere.
Step 2: Generate the Sitemap Index
The sitemap index acts as a directory for individual content sitemaps (e.g., posts, pages, categories).
function generate_custom_sitemap_index() {
$sitemaps = [
'custom-post-sitemap.xml' => '2025-01-31T15:44:00+00:00',
'custom-page-sitemap.xml' => '2024-12-27T14:33:00+00:00',
'custom-category-sitemap.xml' => '2025-01-31T15:44:00+00:00',
'custom-author-sitemap.xml' => '2025-01-27T15:53:00+00:00',
];
header('Content-Type: application/xml; charset=utf-8');
echo '<?xml version="1.0" encoding="UTF-8"?>';
echo '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
foreach ($sitemaps as $slug => $lastmod) {
$url = site_url($slug);
echo "<sitemap>";
echo "<loc>$url</loc>";
echo "<lastmod>$lastmod</lastmod>";
echo "</sitemap>";
}
echo '</sitemapindex>';
exit;
}
Step 3: Create Content-Specific Sitemaps
For each content type (e.g., posts, pages), generate a corresponding XML file. Replace the domain in URLs when needed, such as for reverse proxies.
function generate_custom_sitemap($type) {
global $wpdb;
header('Content-Type: application/xml; charset=utf-8');
echo '<?xml version="1.0" encoding="UTF-8"?>';
echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
$site_url = get_site_url();
switch ($type) {
case 'post':
$posts = $wpdb->get_results("SELECT ID, post_modified_gmt FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type = 'post'");
foreach ($posts as $post) {
$url = str_replace($site_url, 'https://proxy-domain.com', get_permalink($post->ID));
$lastmod = gmdate('Y-m-d\TH:i:s+00:00', strtotime($post->post_modified_gmt));
echo "<url><loc>$url</loc><lastmod>$lastmod</lastmod></url>";
}
break;
case 'page':
$pages = $wpdb->get_results("SELECT ID, post_modified_gmt FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type = 'page'");
foreach ($pages as $page) {
$url = str_replace($site_url, 'https://proxy-domain.com', get_permalink($page->ID));
$lastmod = gmdate('Y-m-d\TH:i:s+00:00', strtotime($page->post_modified_gmt));
echo "<url><loc>$url</loc><lastmod>$lastmod</lastmod></url>";
}
break;
// Additional cases for categories and authors...
}
echo '</urlset>';
exit;
}
Step 4: Prevent Trailing Slash Issues
To avoid WordPress redirecting sitemap URLs to versions with trailing slashes, disable canonical redirects for sitemap paths.
add_filter('redirect_canonical', function ($redirect_url, $requested_url) {
$sitemap_paths = [
'custom-sitemap_index.xml',
'custom-post-sitemap.xml',
'custom-page-sitemap.xml',
'custom-category-sitemap.xml',
'custom-author-sitemap.xml',
];
foreach ($sitemap_paths as $path) {
if (strpos($requested_url, $path) !== false) {
return false; // Disable redirect
}
}
return $redirect_url;
}, 10, 2);
Step 5: Add Rewrite Rules
Register custom rewrite rules to make WordPress recognise sitemap URLs.
function add_custom_sitemap_rewrite_rules() {
add_rewrite_rule('^custom-sitemap_index\.xml$', 'index.php?custom_sitemap=index', 'top');
add_rewrite_rule('^custom-post-sitemap\.xml$', 'index.php?custom_sitemap=custom-post-sitemap.xml', 'top');
add_rewrite_rule('^custom-page-sitemap\.xml$', 'index.php?custom_sitemap=custom-page-sitemap.xml', 'top');
add_rewrite_rule('^custom-category-sitemap\.xml$', 'index.php?custom_sitemap=custom-category-sitemap.xml', 'top');
add_rewrite_rule('^custom-author-sitemap\.xml$', 'index.php?custom_sitemap=custom-author-sitemap.xml', 'top');
}
add_action('init', 'add_custom_sitemap_rewrite_rules');
Flush the rewrite rules after activation by visiting Settings > Permalinks and clicking Save Changes.
Testing
Once the plugin is activated, verify the following URLs:
/custom-sitemap_index.xml
/custom-post-sitemap.xml
/custom-page-sitemap.xml
Check the <loc>
tags in the XML files to ensure the URLs are correct, especially if a reverse proxy is in use.
Leave a Reply