Skip to main content

Handling Redirects and Sitemaps

To manage legacy URL migrations and provide search engines with structured site maps, this project provides the redirects and sitemaps applications. These tools allow you to handle 404 errors by redirecting to new content and generate XML sitemaps dynamically from your database.

Managing Redirects

To handle 404 errors by redirecting users to new paths, configure the RedirectFallbackMiddleware and manage entries in the Redirect model.

Configuring the Redirects App

The redirects application requires the sites framework to be installed. Add both to your INSTALLED_APPS and include the middleware at the end of your MIDDLEWARE list.

# settings.py
INSTALLED_APPS = [
"django.contrib.sites",
"django.contrib.redirects",
# ...
]

MIDDLEWARE = [
# ... other middleware ...
"django.contrib.redirects.middleware.RedirectFallbackMiddleware",
]

SITE_ID = 1

Creating Redirects

You can create redirects programmatically using the Redirect model. The RedirectFallbackMiddleware checks this model whenever a request results in a 404 response.

from django.contrib.redirects.models import Redirect
from django.contrib.sites.models import Site

# Create a 301 Permanent Redirect
Redirect.objects.create(
site=Site.objects.get_current(),
old_path="/old-page/",
new_path="/new-page/"
)

# Create a 410 Gone response (by leaving new_path empty)
Redirect.objects.create(
site=Site.objects.get_current(),
old_path="/obsolete-page/",
new_path=""
)

Customizing Redirect Behavior

If you need to return different status codes (e.g., 302 instead of 301), you can subclass RedirectFallbackMiddleware and override the response classes.

from django.contrib.redirects.middleware import RedirectFallbackMiddleware
from django.http import HttpResponseRedirect, HttpResponseForbidden

class CustomRedirectMiddleware(RedirectFallbackMiddleware):
# Use 302 Found instead of 301 Permanent Redirect
response_redirect_class = HttpResponseRedirect
# Use 403 Forbidden instead of 410 Gone
response_gone_class = HttpResponseForbidden

Generating Sitemaps

To generate XML sitemaps for search engines, use the Sitemap base class or the GenericSitemap helper.

Creating a Custom Sitemap

Define a class inheriting from Sitemap and implement the items() method. You can also provide location(), lastmod(), changefreq(), and priority().

from django.contrib.sitemaps import Sitemap
from myapp.models import Entry

class BlogSitemap(Sitemap):
changefreq = "never"
priority = 0.5

def items(self):
return Entry.objects.filter(is_public=True)

def lastmod(self, obj):
return obj.updated_at

def location(self, obj):
return f"/blog/{obj.slug}/"

Using GenericSitemap for QuerySets

For simple cases where you want to map a QuerySet directly, use GenericSitemap.

from django.contrib.sitemaps import GenericSitemap
from myapp.models import Product

info_dict = {
"queryset": Product.objects.all(),
"date_field": "last_modified",
}

product_sitemap = GenericSitemap(info_dict, priority=0.6, changefreq="weekly")

Routing Sitemaps in URLs

Wire your sitemaps into your URL configuration using the django.contrib.sitemaps.views.sitemap view.

from django.contrib.sitemaps import views
from django.urls import path

sitemaps = {
"blog": BlogSitemap,
"products": product_sitemap,
}

urlpatterns = [
path(
"sitemap.xml",
views.sitemap,
{"sitemaps": sitemaps},
name="django.contrib.sitemaps.views.sitemap",
),
]

Handling Internationalization (i18n)

The Sitemap class supports generating URLs for all enabled languages by setting i18n = True.

class MultilingualSitemap(Sitemap):
i18n = True
alternates = True
x_default = True

def items(self):
return MyModel.objects.all()

Troubleshooting and Gotchas

Missing Sites Framework

RedirectFallbackMiddleware will raise an ImproperlyConfigured error if django.contrib.sites is not in your INSTALLED_APPS. Both redirects and sitemaps rely on the Site model to resolve domains and associate paths.

Middleware Order

RedirectFallbackMiddleware only processes responses that have already been marked as 404. It should generally be placed near the end of your MIDDLEWARE list to ensure other views or middleware have had a chance to handle the request first.

Trailing Slashes

If settings.APPEND_SLASH is True, the middleware will attempt to match the requested path both as-is and with a trailing slash appended if the initial lookup fails.

Sitemap Limits

The Sitemap class enforces a limit of 50,000 items per page, as defined by the sitemap protocol. If your items() returns more than this, use the django.contrib.sitemaps.views.index view to create a sitemap index file.