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.

Experience

Information Technology Consulting

Independent Practice

Provides IT consulting services focused on infrastructure planning, cloud migration strategy, and systems architecture. Engagements draw on years of hands-on sysadmin and development experience across Linux, Windows, and hybrid environments.

K9 Search & Rescue Volunteer

Ongoing

Active participant in K9 Search & Rescue operations, combining technical logistics skills with field support for canine search teams.

Karl Katzke's Blog

October 2006 – May 2014

Published a long-running personal technology blog covering cloud vs. in-house infrastructure, F# and Mono on OSX, hardware vendor critiques, RAID card performance analysis, and sysadmin storytelling. Notable posts include "When Sysadmins Ruled the Earth" (May 15, 2014) and "Getting Started with F# and Mono on OSX" (December 22, 2012).

Credentials

A small badge icon with a shield shape in muted blue tones on a light background

Systems Administration

Deep experience with Linux (RHEL, SLES, CentOS), high-availability clusters, and STONITH configurations.

A small badge icon with a gear shape in muted blue tones on a light background

Cloud Infrastructure

Practical knowledge of AWS EC2, reserved instances, and cost analysis for cloud vs. on-premises deployments.

A small badge icon with a code symbol in muted blue tones on a light background

Development

Proficient in F#, PHP (Symfony), and cross-platform tooling including Mono and MonoDevelop on OSX.

Studies

F# & Functional Programming

Self-directed, 2012

Explored strongly typed functional programming with F# on OSX using the Mono runtime. Published a detailed getting-started guide covering toolchain setup and cross-platform game development research.

High-Availability & Cluster Management

Professional Development, 2009

Configured and documented crm_mon email alerting for STONITH events on SLES11-HAE clusters, integrating with Nagios monitoring for production environments.

Hardware & Storage Performance

Ongoing

Conducted hands-on benchmarking of SATA/SAS RAID controllers including HighPoint RocketRaid 2740 and LSI/SuperMicro AOC-USASLP2-H8iR, comparing against software RAID configurations.

Skills

A small icon representing a server with clean geometric lines in slate blue

Linux Administration

RHEL, SLES, CentOS — package management, kernel tuning, HA clustering, and monitoring integration.

A small icon representing a cloud shape with clean geometric lines in slate blue

Cloud Architecture

AWS EC2, reserved-instance planning, cost modeling, and hybrid infrastructure strategy.

A small icon representing code brackets with clean geometric lines in slate blue

F# & .NET/Mono

Functional programming on OSX, MonoDevelop toolchain, and cross-platform game-dev exploration.

A small icon representing a database cylinder with clean geometric lines in slate blue

PHP & Symfony

Web application development with the Symfony framework and the broader PHP ecosystem.

A small icon representing a storage drive with clean geometric lines in slate blue

Storage & RAID

SATA/SAS controller evaluation, md RAID configuration, and performance benchmarking.

A small icon representing a shield with clean geometric lines in slate blue

High Availability

Pacemaker, STONITH, crm_mon alerting, and Nagios integration for production cluster monitoring.