Guide: Sitemaps¶
SeoSitemap is a thin subclass of Django's django.contrib.sitemaps.Sitemap.
Its key property: the <loc> URL for each item is taken from the item's
resolved SEO canonical — so the sitemap URL always matches the page's
rel="canonical". This eliminates a common hard-to-spot SEO bug where
sitemap URLs and canonical tags diverge.
Basic sitemap¶
# sitemaps.py
from seo_suite.sitemaps import SeoSitemap
from .models import Article
class ArticleSitemap(SeoSitemap):
changefreq = "weekly"
priority = 0.8
def items(self):
return Article.objects.filter(published=True)
# urls.py
from django.contrib.sitemaps.views import sitemap
from .sitemaps import ArticleSitemap
sitemaps = {"articles": ArticleSitemap}
urlpatterns = [
path("sitemap.xml", sitemap, {"sitemaps": sitemaps}),
]
Automatic lastmod¶
SeoSitemap checks the following attributes on each item in order and uses
the first one it finds as <lastmod>:
updated_at → modified → date_modified → updated → last_modified
If none of these attributes exist, <lastmod> is omitted. Models with a
common updated_at = DateTimeField(auto_now=True) work without extra setup.
Multilingual sitemap with hreflang alternates¶
Use Django's built-in sitemap i18n attributes alongside SeoSitemap:
class ArticleSitemap(SeoSitemap):
i18n = True
alternates = True
x_default = True
changefreq = "weekly"
def items(self):
return Article.objects.filter(published=True)
With i18n = True, Django generates one <url> per item per active language.
alternates = True adds <xhtml:link rel="alternate"> entries for each
language. x_default = True adds the x-default alternate.
Because each <loc> comes from the object's get_seo_metadata().canonical_url
(via SeoSitemap.location()), the canonical URL in the sitemap is always in
sync with the canonical URL rendered on the page.
How SeoSitemap.location works¶
For an item that has SeoModelMixin, location() calls get_seo_metadata()
and extracts canonical_url. Only the path and query string are used (scheme
and host are added by Django's sitemap framework from the current Site
object).
If the item has no get_seo_metadata(), or if canonical_url is not set,
location() falls back to Django's default behaviour (calling
item.get_absolute_url()).
Multiple sitemaps¶
Combine several sitemaps in urls.py:
from django.contrib.sitemaps.views import sitemap
from .sitemaps import ArticleSitemap, CategorySitemap, PageSitemap
sitemaps = {
"articles": ArticleSitemap,
"categories": CategorySitemap,
"pages": PageSitemap,
}
urlpatterns = [
path("sitemap.xml", sitemap, {"sitemaps": sitemaps}),
]
Sitemap index¶
For large sites, use Django's SitemapIndexSite or just split sitemaps
normally — SeoSitemap has no restrictions on the number of items.
Static pages in a sitemap¶
For model-less pages, create a simple sitemap that returns paths:
from django.contrib.sitemaps import Sitemap
class StaticViewSitemap(Sitemap):
priority = 0.5
def items(self):
return ["/about/", "/contact/", "/privacy/"]
def location(self, item):
return item
SeoSitemap is only needed when items have get_seo_metadata(). For plain
static paths, Django's built-in Sitemap is sufficient.
Pointing robots.txt at your sitemaps¶
Once your sitemap URL is stable, add it to ROBOTS_SITEMAP_URLS so crawlers
can always find it via robots.txt:
The URL is appended as a Sitemap: directive to every served robots.txt
without requiring you to edit version content. See the
versioned robots.txt guide for the full workflow.