PHPackages                             xwp/custom-xml-sitemap - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. xwp/custom-xml-sitemap

ActiveWordpress-plugin[Utility &amp; Helpers](/categories/utility)

xwp/custom-xml-sitemap
======================

Custom taxonomy-based XML sitemap generator for WordPress.

v1.0.1(2mo ago)02[1 issues](https://github.com/xwp/custom-xml-sitemap/issues)GPL-2.0-or-laterPHPPHP ^8.4CI passing

Since May 12Pushed 2mo agoCompare

[ Source](https://github.com/xwp/custom-xml-sitemap)[ Packagist](https://packagist.org/packages/xwp/custom-xml-sitemap)[ Docs](https://github.com/xwp/custom-xml-sitemap)[ RSS](/packages/xwp-custom-xml-sitemap/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (2)Dependencies (9)Versions (4)Used By (0)

Custom XML Sitemap
==================

[](#custom-xml-sitemap)

[![Test](https://github.com/xwp/custom-xml-sitemap/actions/workflows/test.yml/badge.svg)](https://github.com/xwp/custom-xml-sitemap/actions/workflows/test.yml)[![Latest Stable Version](https://camo.githubusercontent.com/3fb926f665aa73995f1c23224fe93dccda48b8a67ea3cb092df11d97a3fc850e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7877702f637573746f6d2d786d6c2d736974656d61702e737667)](https://packagist.org/packages/xwp/custom-xml-sitemap)[![License](https://camo.githubusercontent.com/f9d7818349f77ed411c0c282e9d57bb66ac8c3634e2624d2eec85440cd6daa9b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f7877702f637573746f6d2d786d6c2d736974656d61702e737667)](https://github.com/xwp/custom-xml-sitemap/blob/main/composer.json)[![PHP Version](https://camo.githubusercontent.com/4ed964c32c4de47f695a0b1f3de09fd06a906f889b8bbf50603d8076ea589cc1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f7877702f637573746f6d2d786d6c2d736974656d61702f7068702e737667)](https://packagist.org/packages/xwp/custom-xml-sitemap)

A WordPress plugin that generates taxonomy-filtered, hierarchical XML sitemaps with configurable time-based granularity (year / month / day) and a separate Terms mode for taxonomy archive sitemaps. Built for high-traffic publishers on WordPress VIP, with Action Scheduler–backed regeneration, a Memcached-safe XML cache, and a React-based admin UI.

Important

**Best suited for WordPress VIP (or any environment with a reliable background job runner).**

This plugin relies on [Action Scheduler](https://actionscheduler.org/) to regenerate sitemap XML asynchronously when content changes. Sitemap files are never built inline during a front-end request; they're written ahead of time by Action Scheduler workers and served from cached post meta.

If Action Scheduler isn't being driven by a real scheduler (WP VIP cron, a system cron hitting `wp action-scheduler run`, or equivalent), sitemap output **will become stale or stop updating entirely**. The default WP-Cron is best-effort and depends on front-end traffic; it works for low-volume sites but is not recommended for production.

See the [Requirements](#requirements) section for setup details.

Screenshots
-----------

[](#screenshots)

Sitemap list: all configured sitemaps at a glance[![Sitemap list](assets/images/screenshot-sitemap-list.png)](assets/images/screenshot-sitemap-list.png)

Sitemap editor: Posts mode with taxonomy filter and filter mode[![Sitemap editor – Posts mode](assets/images/screenshot-editor-posts-mode.png)](assets/images/screenshot-editor-posts-mode.png)

Sitemap editor: Terms mode for taxonomy archive sitemaps[![Sitemap editor – Terms mode](assets/images/screenshot-editor-terms-mode.png)](assets/images/screenshot-editor-terms-mode.png)

Rendered XML sitemap index (styled with XSL)[![XML sitemap index output](assets/images/screenshot-sitemap-index-xml.png)](assets/images/screenshot-sitemap-index-xml.png)

Terms sitemap output listing category archive URLs[![Terms sitemap output](assets/images/screenshot-sitemap-terms-xml.png)](assets/images/screenshot-sitemap-terms-xml.png)

How it Works
------------

[](#how-it-works)

The plugin creates a custom post type for defining sitemap configurations. Each sitemap can target a specific post type, filter by taxonomy terms, and split content by year, month, or day granularity. Generated XML is cached and served via custom rewrite rules with XSL stylesheets for browser-friendly display.

Key features:

- **Taxonomy Filtering** - Filter sitemap content by categories, tags, or custom taxonomies
- **Configurable Granularity** - Split sitemaps by year, month, or day
- **Hierarchical Structure** - Index sitemap links to year sitemaps, which link to month/day sitemaps
- **Auto-Regeneration** - Automatic updates via Action Scheduler when content changes
- **robots.txt Integration** - Automatically adds sitemap references
- **Image Sitemap Support** - Include images in sitemap entries for Google Image Search
- **News Sitemap Support** - Add Google News publication metadata for news sitemaps

Sitemap Configuration
---------------------

[](#sitemap-configuration)

Each sitemap is configured via the **Sitemap Settings** metabox in the editor. The available fields depend on the selected mode.

### Posts Mode

[](#posts-mode)

FieldDescription**Post Type**The post type to include (e.g. Posts, Pages)**Granularity**Date hierarchy: Year, Month, or Day**Taxonomy Filter**Optionally restrict by a taxonomy (Categories, Tags, or custom)**Filter by Terms**One or more specific terms to match**Filter Mode****Include** (only posts with selected terms) or **Exclude** (all posts except those with selected terms)**Include Images**None / Featured Image Only / All Images**Include News Metadata**Adds Google News `` elementsLeave **Filter by Terms** empty to include all posts regardless of term assignment.

Image and News Sitemaps
-----------------------

[](#image-and-news-sitemaps)

### Image Sitemaps

[](#image-sitemaps)

Enable image metadata in your sitemaps to help Google discover images on your pages. Three modes are available:

ModeDescription**None**No images included (default)**Featured Image Only**Include only the post's featured image**All Images**Include featured image + images from post contentWhen enabled, the sitemap includes `` elements with `` for each image URL:

```

  https://example.com/post/
  2024-01-15T10:30:00+00:00

    https://example.com/wp-content/uploads/photo.jpg

```

Image extraction supports:

- Featured images
- Gutenberg `core/image` blocks
- Classic editor inline `` tags
- Custom blocks via the `cxs_extract_block_images` filter

### News Sitemaps

[](#news-sitemaps)

Enable news metadata for Google News sitemaps. When enabled, each URL entry includes publication details:

```

  https://example.com/breaking-news/

      Example Times
      en

    2024-01-15T10:30:00+00:00
    Breaking News Story
    Technology, Innovation

```

News metadata includes:

- **Publication name** - From site name (trailing parentheticals stripped per Google spec)
- **Language code** - ISO 639 format from WordPress locale
- **Publication date** - ISO 8601 format from post date
- **Title** - Post title
- **Keywords** - Categories and tags (excluding "Uncategorized")

Terms Mode (Taxonomy Archive Sitemaps)
--------------------------------------

[](#terms-mode-taxonomy-archive-sitemaps)

In addition to post-based sitemaps, the plugin supports **Terms Mode** which generates sitemaps listing taxonomy term archive URLs (e.g., `/topics/gaming/`, `/category/tech/`) instead of individual post URLs.

### When to Use Terms Mode

[](#when-to-use-terms-mode)

Terms mode is useful when you want search engines to index your taxonomy archive pages:

- Topic/category landing pages
- Tag archives with curated content
- Custom taxonomy term pages

### Configuration

[](#configuration)

1. Create a new sitemap under **Tools &gt; Custom Sitemaps**
2. Set **Sitemap Mode** to "Taxonomy Terms"
3. Select the **Taxonomy** to include (required)
4. Optionally enable **Hide Empty Terms** to exclude terms with no published posts

### URL Structure

[](#url-structure)

Terms mode sitemaps use a paginated structure:

URL PatternDescription`/sitemaps/{slug}/index.xml`Index sitemap (for &gt;1000 terms) or direct URL list`/sitemaps/{slug}/page-1.xml`First page of term URLs (1000 terms max per page)`/sitemaps/{slug}/page-2.xml`Second page, etc.For taxonomies with 1000 or fewer terms, the index.xml contains all term URLs directly. For larger taxonomies, the index.xml becomes a sitemap index linking to paginated sitemaps.

### Example Output

[](#example-output)

**Index sitemap (small taxonomy):**

```

    https://example.com/topics/gaming/

    https://example.com/topics/technology/

```

**Index sitemap (large taxonomy with pagination):**

```

    https://example.com/sitemaps/topics/page-1.xml

    https://example.com/sitemaps/topics/page-2.xml

```

### Behavioral Notes

[](#behavioral-notes)

- Terms mode sitemaps do **not** include `` elements (term archives have no inherent modification date)
- Terms are ordered alphabetically by name
- Automatic regeneration is triggered when terms are created, edited, or deleted (with 5-minute debounce)
- Image and News options are not applicable in Terms mode

Requirements
------------

[](#requirements)

- PHP 8.4+
- WordPress 6.0+
- [Action Scheduler](https://actionscheduler.org/) (bundled with the plugin via Composer)
- Pretty permalinks enabled

### Action Scheduler must be running

[](#action-scheduler-must-be-running)

All XML generation happens off-request, inside Action Scheduler jobs. The plugin schedules:

- A **recurring job** per published sitemap (regenerates the full set of buckets on a configurable cadence)
- **On-change jobs** triggered by post/term saves, deletes, and meta updates (with a 5-minute debounce per sitemap)

If Action Scheduler isn't being processed, none of those jobs run and the cached XML will go stale. The first request after activation will trigger a queue, but ongoing updates depend on a live worker.

#### WordPress VIP (recommended)

[](#wordpress-vip-recommended)

VIP runs Action Scheduler under its managed cron infrastructure. No additional setup is required; install, activate, configure sitemaps, and the platform handles the rest.

#### Self-hosted / non-VIP

[](#self-hosted--non-vip)

You need to drive Action Scheduler explicitly. Pick **one** of:

- **System cron (recommended for production)**: schedule a frequent CLI invocation:

    ```
    * * * * * cd /path/to/wordpress && wp action-scheduler run --batch-size=50 --batches=5 >/dev/null 2>&1
    ```
- **Long-running worker**: run continuously via a process supervisor (systemd, supervisord, etc.):

    ```
    wp action-scheduler run --batch-size=50 --batches=0
    ```
- **WP-Cron (low-traffic sites only)**: works out of the box but only fires on front-end requests. Not recommended for production: a low-traffic site can lag arbitrarily, and a site that's entirely behind a cache may never trigger it. To use this, ensure `DISABLE_WP_CRON` is unset.

To verify the queue is being drained, watch **Tools → Scheduled Actions** in the WP admin or run `wp action-scheduler status`.

Installation
------------

[](#installation)

### Via Composer (Recommended)

[](#via-composer-recommended)

```
composer require xwp/custom-xml-sitemap
```

The plugin and its dependencies (Action Scheduler) will be installed automatically.

### Manual Installation

[](#manual-installation)

1. Download the latest release zip from [GitHub Releases](https://github.com/xwp/custom-xml-sitemap/releases)
2. Upload the zip file via **Plugins &gt; Add New &gt; Upload Plugin** in WordPress admin
3. Activate the plugin through the 'Plugins' menu

### From Source

[](#from-source)

```
# Clone the repository
git clone https://github.com/xwp/custom-xml-sitemap.git

# Install dependencies
cd custom-xml-sitemap
composer install
pnpm install

# Build assets
pnpm run build
```

Then copy the plugin folder to `/wp-content/plugins/` and activate.

### After Installation

[](#after-installation)

Navigate to **Tools &gt; Custom Sitemaps** in the WordPress admin to create your first sitemap.

Sitemap URLs
------------

[](#sitemap-urls)

Once a sitemap is published, it's accessible at the following URLs.

**Posts Mode (default):**

```
/sitemaps/{slug}/index.xml              # Main index
/sitemaps/{slug}/{year}.xml             # Year index
/sitemaps/{slug}/{year}/{month}.xml     # Month sitemap (monthly granularity)
/sitemaps/{slug}/{year}/{month}/{day}.xml  # Day sitemap (daily granularity)

```

**Terms Mode:**

```
/sitemaps/{slug}/index.xml              # Index or direct URL list
/sitemaps/{slug}/page-{n}.xml           # Paginated term URLs (for large taxonomies)

```

WP-CLI Commands
---------------

[](#wp-cli-commands)

### List Sitemaps

[](#list-sitemaps)

```
wp cxs list [--format=]
```

Displays all configured sitemaps with columns: ID, Slug, Post Type, Taxonomy, **Mode**, Status, and URL Count.

### Generate Sitemaps

[](#generate-sitemaps)

```
wp cxs generate [] [--all] [--year=] [--dry-run]
```

### Show Statistics

[](#show-statistics)

```
wp cxs stats [] [--format=]
```

### Validate Sitemaps

[](#validate-sitemaps)

```
wp cxs validate  [--verbose]
```

Developer Hooks
---------------

[](#developer-hooks)

### `cxs_sitemap_skip_post`

[](#cxs_sitemap_skip_post)

Skip a post when emitting urlset entries. Return `true` to omit the post (and any image/news extensions) from the generated XML. Useful for excluding noindex posts, paywalled content, or posts that fail an external policy check.

```
add_filter( 'cxs_sitemap_skip_post', function( $skip, $post_id ) {
    if ( 'noindex' === get_post_meta( $post_id, 'robots_directive', true ) ) {
        return true;
    }
    return $skip;
}, 10, 2 );
```

Filtering happens at XML output time, not at the query level, so date-bucket counts and `` values in sitemap indexes may still reflect skipped posts. This is an intentional trade-off to avoid `meta_query` JOINs that hurt generation throughput.

### `cxs_extract_block_images`

[](#cxs_extract_block_images)

Extract images from custom Gutenberg blocks for image sitemaps.

```
add_filter( 'cxs_extract_block_images', function( $images, $block_name, $block, $post_id ) {
    if ( 'acme/gallery' === $block_name ) {
        // Extract images from custom gallery block
        $gallery_ids = $block['attrs']['imageIds'] ?? [];
        foreach ( $gallery_ids as $id ) {
            $url = wp_get_attachment_image_url( $id, 'full' );
            if ( $url ) {
                $images[] = [ 'url' => $url ];
            }
        }
    }
    return $images;
}, 10, 4 );
```

### `cxs_excluded_post_types`

[](#cxs_excluded_post_types)

Remove post types from appearing in the sitemap **Post Type** dropdown.

```
add_filter( 'cxs_excluded_post_types', function( $excluded ) {
    $excluded[] = 'page';
    return $excluded;
} );
```

### `cxs_excluded_taxonomies`

[](#cxs_excluded_taxonomies)

Remove taxonomies from the **Taxonomy Filter** and **Terms Mode** dropdowns.

```
add_filter( 'cxs_excluded_taxonomies', function( $excluded ) {
    $excluded[] = 'post_format';
    return $excluded;
} );
```

Local Development &amp; Testing
-------------------------------

[](#local-development--testing)

### Installation

[](#installation-1)

```
composer install
pnpm install
```

### Running Tests

[](#running-tests)

```
# Start wp-env Docker environment
pnpm env:start

# Run PHPUnit tests
pnpm run test:php

# Run PHP Code Sniffer
composer lint

# Run PHPStan
composer phpstan
```

### Build Assets

[](#build-assets)

```
pnpm run build
```

License
-------

[](#license)

GPLv2 or later.

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance86

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

73d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/415bc53586018077324af64dad074bf430ba1b72471550489372668274a13b12?d=identicon)[xwp](/maintainers/xwp)

---

Top Contributors

[![PatelUtkarsh](https://avatars.githubusercontent.com/u/5015489?v=4)](https://github.com/PatelUtkarsh "PatelUtkarsh (40 commits)")

---

Tags

action-schedulerseositemaptaxonomywordpresswordpress-pluginwordpress-vipxml-sitemapwordpressSitemapseotaxonomyxml-sitemap

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/xwp-custom-xml-sitemap/health.svg)

```
[![Health](https://phpackages.com/badges/xwp-custom-xml-sitemap/health.svg)](https://phpackages.com/packages/xwp-custom-xml-sitemap)
```

###  Alternatives

[roots/bedrock

WordPress boilerplate with Composer, easier configuration, and an improved folder structure

6.5k463.8k2](/packages/roots-bedrock)[helsingborg-stad/municipio

A bootstrap theme for creating municipality sites.

4028.5k10](/packages/helsingborg-stad-municipio)[roots/wp-stage-switcher

WordPress plugin that allows you to switch between different environments from the admin bar

375469.2k3](/packages/roots-wp-stage-switcher)[vinkla/wordplate

The WordPlate boilerplate

2.2k5.3k](/packages/vinkla-wordplate)[sybrew/the-seo-framework-extension-manager

A WordPress plugin that allows you to manage extensions for The SEO Framework.

8894.3k](/packages/sybrew-the-seo-framework-extension-manager)[wanze/seo-maestro

Seo Maestro allows to easily manage your page's meta data and sitemap.

373.4k](/packages/wanze-seo-maestro)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
