Writing a PHP Script to Generate Dynamic Sitemaps
A sitemap is a small XML document that tells search engines which URLs belong to a website, when pages were changed, and how those pages relate to the site. For a content-heavy PHP application, generating that document from the database is more reliable than maintaining a static file by hand.
A dynamic sitemap is useful for blogs, product catalogues, documentation sites, and service businesses whose pages change regularly. It can include published articles, categories, profiles, and other indexable resources while excluding private records, duplicate routes, and temporary pages.
The basic implementation is straightforward: retrieve approved URLs, escape their values for XML, output a valid sitemap document, and send the correct HTTP headers. A production-ready version also needs URL limits, caching, database efficiency, and sensible deployment practices.
Decide Which URLs Belong In The Sitemap
Start by defining the pages that search engines should discover. A blog might include published posts and public category pages, while excluding drafts, login screens, internal search results, filtered duplicates, and administrative routes. The sitemap should describe the canonical URL structure rather than every route the application happens to serve.
For an Australian business, canonical URLs should use the primary domain and HTTPS consistently, whether visitors arrive from Sydney, Melbourne, Perth, or regional areas. If a site serves customers in multiple states, location pages can be included when they provide genuinely distinct information. Near-identical pages created solely for keyword variation usually create unnecessary maintenance.
A sitemap index becomes useful when a site has more than 50,000 URLs or approaches the 50 MB uncompressed XML limit. Separate files such as posts-sitemap.xml, products-sitemap.xml, and locations-sitemap.xml make large datasets easier to manage and troubleshoot.
Retrieve Published Records Safely
Use a database query that selects only records eligible for indexing. A typical query includes a publication state, a non-null slug, and an optional publication date that is not in the future. Parameterised queries through PDO keep the data access layer safer and easier to test.
$pdo = new PDO($dsn, $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$sql = <<<SQL
SELECT slug, updated_at
FROM articles
WHERE status = :status
AND published_at <= UTC_TIMESTAMP()
ORDER BY id ASC
SQL;
$stmt = $pdo->prepare($sql);
$stmt->execute(['status' => 'published']);
Selecting only the fields required for the XML response reduces memory use. On a busy site, avoid loading hundreds of thousands of rows into an array. Stream records where practical, or process them in batches using an indexed primary key. An Australian site hosted in Sydney or Brisbane may still serve international crawlers, so predictable database performance matters regardless of local traffic patterns.
Produce Valid XML From PHP
The response must declare the XML content type and include the sitemap namespace. Every dynamic value needs XML escaping. htmlspecialchars() with ENT_XML1 is suitable for URL and date values, provided the application has already normalised its data.
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">';
while ($article = $stmt->fetch()) {
$url = 'https://example.com/articles/' . rawurlencode($article['slug']);
$lastmod = gmdate('c', strtotime($article['updated_at']));
echo '<url>';
echo '<loc>' . htmlspecialchars($url, ENT_XML1, 'UTF-8') . '</loc>';
echo '<lastmod>' . htmlspecialchars($lastmod, ENT_XML1, 'UTF-8') . '</lastmod>';
echo '</url>';
}
echo '</urlset>';
The URL builder should use one canonical base URL from configuration rather than trusting the incoming host header. This prevents malformed sitemap entries when the application is accessed through an alternate hostname, a staging domain, or an incorrectly configured reverse proxy.
lastmod should represent a meaningful content change. Changing it whenever a page is viewed gives search engines misleading information. Store a reliable update timestamp, use UTC in the database, and format the value as an ISO 8601 date or timestamp.
Handle Slugs, Domains, And Localisation
Slugs should be unique, stable, and restricted to the format supported by the application. If a page title changes, changing the slug can create a broken link and a new sitemap entry. A redirect from the old path to the canonical URL is usually preferable.
When a website has language or regional versions, define the relationship between them carefully. Australian spelling, local pricing in dollars, and state-specific service information can justify separate pages, but each version should have a clear canonical address. For a site aimed at people comparing services in Adelaide or Canberra, location data should be accurate rather than generated from a list of suburbs.
External references can be included as ordinary content links, but they do not belong in the sitemap unless they are pages hosted by the site itself. For example, a site review or personal profile may naturally point readers towards Being Zhenya, while the sitemap should contain only the locally controlled page carrying that reference.
Add Caching Without Stale Results
Generating XML from the database on every crawler request can create avoidable load. A simple file cache, an application cache, or a reverse proxy can serve the same sitemap for several minutes or hours. The correct duration depends on how often the site publishes new material.
You can also send an ETag or Last-Modified header and respond with HTTP 304 when the sitemap has not changed. This reduces bandwidth and processing for repeat requests. A site publishing around the Australian business day may use a shorter cache during editorial hours and a longer period overnight.
Cache invalidation should happen when a page is published, unpublished, deleted, or materially updated. If that event-driven approach is unavailable, a scheduled task can rebuild the sitemap every few hours. On a Linux server, a cron job can call a command-line PHP script and log failures for later review.
Make Large Sitemaps Efficient
For a small blog, a single endpoint is sufficient. Larger installations should generate multiple sitemap files and a sitemap index. Each child file must remain within the protocol limits, and the index should list the location and modification date of every child document.
A batch-oriented generator can track the last processed database identifier instead of using a large offset. Indexes on status, published_at, and the ordering column help the database find eligible records efficiently. Avoid joining large tables unless the additional data is required to construct the final canonical URL.
Infrastructure planning becomes important when sitemap generation shares a server with application traffic. A consultant working on infrastructure planning services would typically examine database load, deployment workflows, monitoring, and failure recovery before recommending where generation should run.
Test, Publish, And Monitor The Endpoint
Test the output with an XML parser before sending it to search engines. Common errors include an unescaped ampersand, invalid characters copied from a title, an incorrect namespace, malformed timestamps, and accidental debug output before the XML declaration. A PHP warning printed into the response can invalidate the entire document.
Check that every listed URL returns the intended status code, resolves over HTTPS, and points to the canonical address. Confirm that draft pages, deleted articles, query-string variations, and staging domains are absent. Submit the sitemap index or sitemap URL in the relevant webmaster tools and reference it in robots.txt:
Sitemap: https://example.com/sitemap.xml
Monitoring should alert you when the endpoint returns a 500 error, produces zero URLs unexpectedly, or falls sharply below the normal page count. Logs should include generation time and record count without exposing database credentials. Test daylight-saving transitions as well, since Australian states use different daylight-saving rules and local server settings can produce confusing timestamps.
Build the generator as a small, testable component rather than embedding it throughout the application. With clean URL rules, safe XML output, sensible caching, and basic monitoring, a PHP sitemap endpoint can remain dependable as a site grows. Add it to your deployment and publishing workflow, validate the generated XML, and keep the sitemap aligned with the pages you genuinely want discovered.
Karl Katzke