Build Your Own XML Sitemap Generator in PHP

Santaji GadeSEOTechnical SEO2 weeks ago39 Views

Most sitemap plugins are black boxes. Here's real, complete PHP code for four working sitemap generators from a basic script to a database-driven version that handles the 50,000 URL limit automatically.

Technical SEO Sitemap Generator PHP XML

Most sitemap plugins are black boxes. You install one, hope it catches every URL correctly, and rarely look at what it actually outputs. Building your own PHP sitemap generator takes about thirty minutes and gives you full control over exactly which pages get included, how often they update, and what happens once you cross 50,000 URLs.

A sitemap generator built in PHP reads a list of URLs, either from an array, a directory scan, or a database query, then outputs valid XML following the official sitemaps.org protocol that search engines expect.

This guide walks through four working sitemap generator versions, from a basic static script to a database-driven generator that handles sitemap indexes automatically.

We covered why this file matters in our XML sitemap guide. This article is the hands-on build: real, complete PHP code you can run today.

Advertisement
Advertisement
50,000

maximum URLs allowed in a single sitemap file per the sitemaps.org protocol

50MB

maximum uncompressed file size per sitemap file before splitting is required

4

working sitemap generator versions covered, from basic to fully automated

How This Sitemap Generator Pipeline Works

STEP 1Collect URLs
STEP 2Build XML with DOMDocument
STEP 3Split if Over 50,000
STEP 4Save and Automate

Version 1: A Basic Sitemap Generator From an Array

This is the simplest working sitemap generator version, using PHP's built-in DOMDocument class rather than string concatenation, since DOMDocument automatically escapes special characters correctly.

<?php
header('Content-Type: application/xml; charset=utf-8');

$baseUrl = 'https://www.brandella.in';

$urls = [
    ['loc' => '/', 'lastmod' => '2026-07-30', 'changefreq' => 'daily', 'priority' => '1.0'],
    ['loc' => '/blog/', 'lastmod' => '2026-07-29', 'changefreq' => 'daily', 'priority' => '0.9'],
    ['loc' => '/about/', 'lastmod' => '2026-06-15', 'changefreq' => 'monthly', 'priority' => '0.6'],
];

$xml = new DOMDocument('1.0', 'UTF-8');
$xml->formatOutput = true;

$urlset = $xml->createElement('urlset');
$urlset->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
$xml->appendChild($urlset);

foreach ($urls as $entry) {
    $urlNode = $xml->createElement('url');
    $urlNode->appendChild($xml->createElement('loc', $baseUrl . $entry['loc']));
    $urlNode->appendChild($xml->createElement('lastmod', $entry['lastmod']));
    $urlNode->appendChild($xml->createElement('changefreq', $entry['changefreq']));
    $urlNode->appendChild($xml->createElement('priority', $entry['priority']));
    $urlset->appendChild($urlNode);
}

echo $xml->saveXML();

Save as sitemap.php and visit it directly in a browser to see valid XML output

Advertisement
Advertisement

Version 2: Scanning a Directory Automatically

For static sites, scanning a folder of HTML files with PHP's RecursiveDirectoryIterator removes the need to maintain a URL list by hand entirely.

<?php
function scanDirectory($dir, $extension = 'html') {
    $urls = [];
    $files = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
    );

    foreach ($files as $file) {
        if ($file->getExtension() === $extension) {
            $relativePath = str_replace($dir, '', $file->getPathname());
            $urls[] = [
                'loc' => str_replace('\\', '/', $relativePath),
                'lastmod' => date('Y-m-d', $file->getMTime()),
                'changefreq' => 'weekly',
                'priority' => '0.5'
            ];
        }
    }
    return $urls;
}

$urls = scanDirectory(__DIR__ . '/public');

Automatically finds every HTML file, using the file's own modified date for lastmod

Version 3: Pulling URLs From a Database

For dynamic sites built on a CMS or custom database, querying published content directly through PHP's PDO extension keeps the sitemap perfectly in sync with what actually exists.

<?php
$pdo = new PDO('mysql:host=localhost;dbname=brandella;charset=utf8mb4', 'db_user', 'db_password');
$stmt = $pdo->query("SELECT slug, updated_at FROM posts WHERE status = 'published'");

$urls = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    $urls[] = [
        'loc' => '/' . $row['slug'] . '/',
        'lastmod' => date('Y-m-d', strtotime($row['updated_at'])),
        'changefreq' => 'weekly',
        'priority' => '0.7'
    ];
}

Replace the query with whatever table structure your own CMS or app actually uses

Advertisement
Advertisement

Handling More Than 50,000 URLs

The sitemaps.org protocol caps a single file at 50,000 URLs. Larger sites need a sitemap index file pointing to multiple chunked sitemaps.

A single sitemap.xml file, exactly like Version 1 above, is all that's needed. No index file is required.

Split the URL array into chunks of 50,000 using PHP's array_chunk function, generate one sitemap file per chunk, then build a sitemap index file listing each chunked file's location.

<?php
$chunks = array_chunk($allUrls, 50000);

$indexXml = new DOMDocument('1.0', 'UTF-8');
$indexXml->formatOutput = true;
$sitemapindex = $indexXml->createElement('sitemapindex');
$sitemapindex->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
$indexXml->appendChild($sitemapindex);

foreach ($chunks as $i => $chunk) {
    file_put_contents("sitemap-" . ($i + 1) . ".xml", buildSitemapXml($chunk));

    $sitemap = $indexXml->createElement('sitemap');
    $sitemap->appendChild($indexXml->createElement('loc', $baseUrl . "/sitemap-" . ($i + 1) . ".xml"));
    $sitemap->appendChild($indexXml->createElement('lastmod', date('Y-m-d')));
    $sitemapindex->appendChild($sitemap);
}

file_put_contents('sitemap-index.xml', $indexXml->saveXML());

Wrap the Version 1 logic in a buildSitemapXml() function, then call it once per chunk here

Automating Generation With CRON

1

Save the Script to Write a File, Not Just Echo

Change the final line from echo $xml->saveXML(); to file_put_contents('sitemap.xml', $xml->saveXML()); so running the script regenerates the actual file on disk.

2

Add a CRON Job

A daily cron entry like 0 2 * * * php /path/to/sitemap-generator.php regenerates the sitemap automatically every night at 2am.

3

Reference It in robots.txt

Add Sitemap: https://www.brandella.in/sitemap.xml to your robots.txt file so crawlers can discover the generated file automatically.

Live XML Sitemap Preview Builder

Paste URLs, one per line, to see the exact XML this script would generate.

Live XML Sitemap Preview Builder

Mirrors the output of the PHP script above, without needing to run PHP

Your generated sitemap.xml will appear here.

Validating Your Sitemap Generator's Output

Before relying on any sitemap generator in production, confirm the output actually validates.

According to Google Search Central's guide to building a sitemap, submitting the file through Search Console's Sitemaps report is the most reliable way to confirm Google can parse it without errors.

A quick manual check works too: open the generated file directly in a browser.

A well-formed sitemap generator produces XML the browser renders as a structured tree, while a broken one shows a parsing error at the exact line where the invalid character or malformed tag sits.

When to Choose a Custom Generator Over a Plugin

According to Yoast's developer blog on building a custom sitemap script, a hand-built sitemap generator makes the most sense when a site has an unusual content structure, like large volumes of downloadable files, that generic plugins do not handle well by default.

For a standard WordPress blog with no unusual content types, an established plugin usually remains the more practical choice, since it also handles edge cases like taxonomy pages and media attachments without any custom code required.

Performance Considerations for Large Sites

A sitemap generator scanning tens of thousands of database rows or files can consume significant memory if not handled carefully.

According to an open-source PHP sitemap generator reference implementation, generation time scales directly with site size, so very large catalogs benefit from batching database queries rather than loading every URL into memory at once.

According to PHP's own documentation for array_chunk, this same batching function is what splits URLs into manageable groups for both memory efficiency and the 50,000 URL sitemap limit.

Running the sitemap generator as a scheduled background job, rather than triggering it from a live page request, keeps this resource usage isolated from actual visitor traffic.

Common Mistakes When Building a Sitemap Generator

MistakeWhy It Matters
Using string concatenation instead of DOMDocumentSpecial characters in URLs can break XML validity
Including noindexed or redirected URLsSends contradictory signals about which pages matter
Forgetting the 50,000 URL limitLarge sites need a sitemap index, not one giant file
Never automating regenerationA stale sitemap missing new content defeats its purpose
Not referencing the file in robots.txtMakes discovery slower for crawlers checking that file first

FAQs on Building a PHP Sitemap Generator

Do I need a framework to build a sitemap generator in PHP?
No. Plain PHP with the built-in DOMDocument class handles this entirely, without any external library or framework dependency for a basic sitemap generator.
Why use DOMDocument instead of just writing the XML as a string?
DOMDocument automatically escapes special characters like ampersands and quotes correctly. Manual string concatenation can silently produce invalid XML if a URL or title contains such characters.
How do I handle a site with more than 50,000 pages?
Split the URL list into chunks of 50,000 using array_chunk, generate one sitemap file per chunk, and create a sitemap index file that references each chunked file's location.
Should the sitemap generator run on every page load?
No. Generate the file periodically through a CRON job and serve the static result, rather than regenerating it dynamically on every single request, which wastes server resources.
Can this same sitemap generator approach work for WordPress or other CMS platforms?
Yes, by adapting the database query in Version 3 to match your CMS's actual table structure, such as WordPress's wp_posts table, instead of a custom posts table.
How do I test that the generated sitemap is valid?
Open the file directly in a browser to confirm it renders as XML without errors, then submit it through Google Search Console's Sitemaps report to confirm Google can parse it correctly.

> what_we_learn_today.log

[OK]

DOMDocument builds valid XML more safely than manual string concatenation

[OK]

A directory scanner removes the need to maintain a URL list by hand

[OK]

Database-driven generation keeps dynamic sites perfectly in sync

[OK]

50,000 URLs per file is the hard limit; use a sitemap index beyond that

[OK]

CRON automation keeps the file fresh without manual regeneration

[OK]

Always validate output before relying on it for production traffic

0 Votes: 0 Upvotes, 0 Downvotes (0 Points)

Leave a reply

Loading Next Post...
Search
Popular Now
Loading

Signing-in 3 seconds...

Signing-up 3 seconds...