Proper Sitemap Update Dates for Eleventy
Sitemaps are the recommended way to ensure search engines understand all the pages on your site. This site is pretty simple and I'm not sure whether a sitemap is strictly needed, but I had one with WordPress and I'd like to keep it going. One important piece for this 23 year old blog is signalling when old posts are updated, so I had to make sure my new Eleventy-powered site handled that piece correctly.
Sitemaps
First of all what are sitemaps? From the Google developer site:
A sitemap is a file where you provide information about the pages, videos, and other files on your site, and the relationships between them. Search engines like Google read this file to crawl your site more efficiently. A sitemap tells search engines which pages and files you think are important in your site, and also provides valuable information about these files.
In short it's an XML file you can submit to search engines like Google. This site's sitemap lives at https://www.cantoni.org/sitemap.xml. In its simplest form it's a collection of links (<loc>
) along with the date last updated (<lastmod>
).
The Eleventy static site system doesn't come with a built-in sitemap template, but there are plenty of examples out there. Since I used the 11ty/eleventy-base-blog starter project, it has a built-in Nunjucks template that looks like this:
---
permalink: /sitemap.xml
layout: false
eleventyExcludeFromCollections: true
---
<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
{%- for page in collections.all %} {% set absoluteUrl %}{{ page.url | htmlBaseUrl(metadata.url) }}{% endset %} <url>
<loc>{{ absoluteUrl }}</loc>
<lastmod>{{ page.date | htmlDateString }}</lastmod>
</url> {%- endfor %}
</urlset>
Notice the <lastmod>
element there which is reflecting the last modified date for that content. This template uses the post date, but that's a problem because that date will never change, even if you make edits to the post. WordPress stores everything in a database and can keep track of created versus modified times. However, for this static site we do have a database of sorts since everything is stored in git. With the help of GitHub copilot, let's try to use the git "last modified" time to improve our sitemap.
First attempt
I'm still learning the Eleventy system and in particular the data model. My first thought was to create a page variable that has the last-modified date as found with the git log
command. This was my prompt:
I want to add a new page metadata variable for this Eleventy blog with the following details:
- During the build process, create a new variable
lastmod
that will be usable by templates aspage.lastmod
- The
lastmod
variable should be determined by calling out to the shell with the command:git log -1 --format=%cd --date=iso-strict PAGE
where PAGE is thepage.inputPath
value- Just make the javascript code changes; I will modify the template separately
The JavaScript code changes Copilot created looked great, but this didn't work and I spent hours fiddling around with it. The git log
part was working, but it seems you can't create a variable on the fly like this (addition to the page metadata). I think my approach here was too low level and was based on my (probably slightly wrong) assumptions about how Eleventy works.
Second attempt
I abandoned that approach, took a break, then decided to start at a higher level by explaining what I wanted to accomplish (rather than how to build it). This time I used Copilot agent mode with full permissions enabled and this new prompt:
I need to fix the build process so that the #file:sitemap.xml.njk template which generates _site/sitemap.xml file sets the "
" value to the date in yyyy-mm-dd format of the last git commit. (Rather than right now which is the page.date value.) The last modified date can be fetched by shelling to git like this: git log -1 --date=format:'%Y-%m-%d' --format="%cd" -- FILENAME. After you make changes, you can run the build with make build. One test value that will show that it's working: in _site/sitemap.xml the entry for "https://www.cantoni.org/2015/03/11/recovering-from-a-wordpress-hack/" should be " 2025-05-28 "
Allowing Copilot to make the changes, run the build, and then check the results seems helpful because it can iterate on its solution. I also gave it one specific older blog post with an expected result.
There are two parts to this solution - first is a new gitLastModified
filter which is pretty straightforward:
// Get the last modified date of a file using git log (used for sitemap.xml)
eleventyConfig.addFilter("gitLastModified", (inputPath) => {
try {
const today = DateTime.now().toFormat("yyyy-MM-dd");
// Return today's date for any templated pages (home page, Atom feed, /blog, /tags, etc.)
if (inputPath.endsWith(".njk")) {
return today;
}
const result = execSync(
`git log -1 --date=format:'%Y-%m-%d' --format="%cd" -- "${inputPath}"`,
{ encoding: "utf-8" }
);
if (result) {
return result.trim();
} else {
return today;
}
} catch (e) {
console.error(`Error getting last modified date for ${inputPath}:`, e);
return null;
}
});
I added the special handling for template-driven pages like the home page, blog archive, tags archive and Atom feed so they will always reflect the build date. Otherwise this automation was using the date the template was edited which is not the intent.
The second step is to use gitLastModified
in the sitemap.xml template:
<lastmod>{{ page.inputPath | gitLastModified }}</lastmod>
It worked!
Final piece
Well it almost worked correctly. While writing this blog post I did a quick check of the live sitemap.xml and found it was wrong: all <lastmod>
dates were the date I last built the whole site. Luckily my human brain solved this one quickly because I realized that CI pipelines (GitHub Actions in this case) usually only do a shallow check out of the repository (just the latest change). Switching this to checking out the full repo history was the quick solution. (This probably wouldn't scale well for a larger repo, but mine is small enough this works fine.)
Now I periodically check the Google Search Console to see if it's reported any errors and so far it's looking good.
- ← Previous
Migrated to Static Site (Eleventy)