Sitemaps & SEO
Search engines find pages by following links and reading a sitemap, not by guessing. Django
ships a small framework, django.contrib.sitemaps, that generates a standards-compliant
XML sitemap straight from your own models — no hand-maintained list of URLs that quietly goes
stale the moment someone adds a new page.
Enabling the Sitemaps Framework
Like most of Django's optional pieces, sitemaps start as an app you add to
INSTALLED_APPS:
# taskflow_project/settings.py
INSTALLED_APPS = [
# ...
'django.contrib.sitemaps',
]
Defining a Sitemap Class
A sitemap is a plain Python class inheriting from Sitemap, with two methods Django
calls for you: items(), returning the QuerySet of objects to include, and
location(obj), returning each one's URL. Most of TaskFlow's tasks belong to private
projects, but suppose a subset are flagged public and worth exposing to search engines:
# taskflow/sitemaps.py
from django.contrib.sitemaps import Sitemap
from .models import Task
class TaskSitemap(Sitemap):
changefreq = "weekly"
priority = 0.5
def items(self):
return Task.objects.filter(is_public=True)
def location(self, obj):
return f"/tasks/{obj.pk}/"
Django calls items() once to get every object to list, then calls
location() for each one to work out its URL. Add a new public task tomorrow, and it
appears in the sitemap on the very next request — nobody has to remember to update a static file.
changefreq and priority
Both class attributes above are hints, not guarantees — search engines are free to ignore them,
but they're cheap to provide and can influence crawl behavior. changefreq tells a
crawler roughly how often a given URL's content tends to change ("always",
"daily", "weekly", "yearly"...), and priority
is a number from 0.0 to 1.0 expressing this URL's importance relative
to other URLs in the same sitemap — it isn't compared across different sites.
Wiring the Sitemap into urls.py
A Sitemap class does nothing until a URL actually serves it. Django provides a
ready-made sitemap view for exactly this — map it to the conventional
/sitemap.xml path, and hand it a dictionary of every sitemap section your project
has (here, just one: tasks):
# taskflow_project/urls.py
from django.contrib.sitemaps.views import sitemap
from taskflow.sitemaps import TaskSitemap
sitemaps = {'tasks': TaskSitemap}
urlpatterns = [
# ... existing paths ...
path('sitemap.xml', sitemap, {'sitemaps': sitemaps},
name='django.contrib.sitemaps.views.sitemap'),
]
Visiting /sitemap.xml now returns a proper XML document listing every public task's
URL, along with its changefreq and priority — exactly what a search
engine expects to find at that conventional address.
Basic On-Page SEO with Template Blocks
A sitemap tells a search engine which pages exist; the page-specific
<title> and meta description tell it (and the visitor scanning search results)
what each one is. Recall base.html from
Template Inheritance & Includes — wrapping its
<title> in a block lets every page override it individually instead of every
page sharing one fixed title:
{# taskflow/templates/base.html #}
<head>
<title>{% block title %}TaskFlow{% endblock %}</title>
<meta name="description" content="{% block meta_description %}Manage projects and tasks with TaskFlow.{% endblock %}">
</head>
{# taskflow/templates/taskflow/task_detail.html #}
{% extends "base.html" %}
{% block title %}{{ task.title }} - TaskFlow{% endblock %}
{% block meta_description %}{{ task.description|truncatewords:20 }}{% endblock %}
{% block content %}
<h1>{{ task.title }}</h1>
{% endblock %}
Every page still shares one base.html, but each one now announces its own specific
title and description in search results, instead of every task page looking identical to a
search engine.
Canonical URLs
A <link rel="canonical"> tag tells a search engine which URL is the
"official" one for a page's content, which matters when the same content is reachable through
more than one URL (with and without a trailing slash, or via a query string, for instance) — the
same pattern this very tutorial site uses on every one of its own pages:
{# inside base.html's head, or overridden per page #}
<link rel="canonical" href="https://taskflow.example.com{{ request.path }}">
Does SEO Even Matter for an App Like TaskFlow?
Worth being honest here: most of TaskFlow sits behind a login. Search engines can't index a project board or someone's private task list, and building out sitemap and meta-tag machinery for pages nobody outside your own team will ever see is effort spent in the wrong place. Where this genuinely pays off is TaskFlow's small public surface — a marketing page, a public project's read-only view — and it pays off completely for a different kind of Django project built to be found: a blog, a documentation site, a marketing site. The techniques are identical either way; only how much of your project actually needs them changes.
/sitemap.xml isn't actually
generated on this page. Add TaskSitemap to your own project's URLs to see the real
XML output.