Generate an XML Sitemap in Craft CMS

Why are sitemaps important?

If a web crawler lands on your home page, how will it know where the rest of your content lives?

Well, it will start by scanning the page for links to other pages. Then it will navigate to those pages and scan them for links as well. But what if you have content that isn't linked to from any of these pages?

You can make sure search engines find all of your content with the help of sitemap. In general, sitemaps speed up the process of content discovery and webpage indexing.

Sitemap generation in Craft CMS

Well, there's a plugin for that. Or two. Or three.

But if you decide to use a plugin, that's one more thing to rely on and keep updated long term. Sometimes all you need is a simple, lightweight solution.

An XML sitemap is just a list of page URLs

We can create our own sitemap by looping through all Craft CMS entries and only outputting published entries that have URLs. Here's a Twig template to do exactly that.

Save this file in your /templates directory and name it sitemap.xml:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="">

  {# Create an empty array for all published entries #}
  {% set entry_list = [] %}

  {# Create a URI exclusion list for entries you don't want in the sitemap #}
  {% set exclusions = ["blog/unlisted-blog-post", "thank-you-page"] %}

  {# Get all sections (singles, channels, and structures) #}
  {% set sections = %}

  {# Loop through each section #}
  {% for section in sections %}

    {# Get all entries of this section type, but only if they're published and have a URL #}
    {% set entries = craft.entries.sectionId("live").uri("not ''").all() %}

    {# Merge these entries into the main list #}
    {% set entry_list = entry_list|merge(entries) %}
  {% endfor %}

    Loop through all published entries to create the sitemap markup.
    Skip any entries with a URI that appears in the exclusions list.
  {% for entry in entry_list if entry.uri not in exclusions %}
      <loc>{{ entry.url }}</loc>
      <lastmod>{{ entry.dateUpdated|date("c") }}</lastmod>
  {% endfor %}


You can view this sitemap by navigating to in a web browser. Replace the domain with your website's domain, of course.

You may need to modify this template to match your website's content or to exclude certain pages. But it's a great starting point for a do-it-yourself XML sitemap.

Ready to discuss your project?

Send me a message and I'll get back to you within one business day.

Let's Talk