Skip to main content

Django Per-Site Cache

Introduction

Django's per-site cache is one of the simplest yet most powerful caching strategies available in the framework. As the name suggests, this approach caches your entire site, making it extremely efficient for sites with content that doesn't change frequently or where most users see the same pages.

In this tutorial, you'll learn:

  • What per-site caching is and when to use it
  • How to implement per-site caching in your Django project
  • How to configure and fine-tune the caching behavior
  • Best practices and potential pitfalls to avoid

What is Per-Site Caching?

Per-site caching is a caching strategy that stores the entire rendered HTML of your web pages. When enabled, Django checks if a requested page exists in the cache:

  • If the page is in the cache, Django returns the cached version without running any views or templates
  • If not in the cache, Django processes the request normally and stores the result in the cache for future requests

This dramatically reduces server load and response times because subsequent requests can be served directly from the cache without executing Python code, querying the database, or rendering templates.

When to Use Per-Site Cache

Per-site caching is ideal for:

  • Content-heavy sites that don't change frequently
  • Sites with high traffic but low personalization
  • Blogs, news sites, or documentation pages
  • Public-facing pages that are the same for all users

It's not suitable for:

  • Pages with user-specific content
  • Forms that require CSRF protection
  • Pages that display real-time data
  • Dynamic applications where content changes frequently

Setting Up Per-Site Cache

Step 1: Configure a Cache Backend

First, you need to configure a cache backend in your settings.py file:

python
# settings.py

CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': '127.0.0.1:11211',
}
}

You can choose from several cache backends:

  • Memcached (recommended for production)
  • Redis
  • Database caching
  • File system caching
  • Local-memory caching (good for development)

For beginners testing locally, you can use the local-memory cache:

python
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}

Step 2: Add the Cache Middleware

To enable per-site caching, you need to add Django's UpdateCacheMiddleware and FetchFromCacheMiddleware to your middleware configuration:

python
# settings.py

MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware', # Must be first in the list
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware', # Must be last in the list
# ... other middleware classes ...
]

Important: The UpdateCacheMiddleware must be the first middleware listed, and the FetchFromCacheMiddleware must be the last one. This ensures that the cache is checked before processing the request and updated after generating the response.

Step 3: Configure Cache Settings

Add these additional cache settings to your settings.py:

python
# settings.py

# The number of seconds each page should be cached
CACHE_MIDDLEWARE_SECONDS = 600 # Cache pages for 10 minutes

# The cache key prefix to use to avoid collisions
CACHE_MIDDLEWARE_KEY_PREFIX = 'mysite'

# Whether to cache anonymous-only or all pages (True = anonymous only)
CACHE_MIDDLEWARE_ALIAS = 'default' # Which cache to use from your CACHES setting

Testing Per-Site Caching

Let's create a simple view to test our caching setup:

python
# views.py
from django.http import HttpResponse
import datetime

def current_time(request):
now = datetime.datetime.now().strftime("%H:%M:%S")
html = f"<html><body>The time is {now}.</body></html>"
return HttpResponse(html)

Add this URL pattern:

python
# urls.py
from django.urls import path
from . import views

urlpatterns = [
path('time/', views.current_time, name='current_time'),
]

Now visit the /time/ URL in your browser. Reload the page several times. You'll notice that the time displayed doesn't change for 10 minutes (or whatever duration you set with CACHE_MIDDLEWARE_SECONDS). This confirms that your page is being cached!

Per-View Caching vs. Per-Site Caching

If you don't want to cache your entire site, Django also provides options for caching individual views:

python
from django.views.decorators.cache import cache_page

@cache_page(60 * 15) # Cache this view for 15 minutes
def my_view(request):
# ...
return response

The main differences:

  • Per-site caching: Caches all pages automatically
  • Per-view caching: Allows you to selectively cache specific views

Adding Cache Vary Headers

Sometimes you need to vary the cache based on certain request headers. For example, if your site serves different content based on the user's language, you can use the Vary header:

python
# settings.py

# Cache varies by Accept-Language header
CACHE_MIDDLEWARE_VARY = ['Accept-Language']

This ensures that different language versions of your site are cached separately.

Clearing the Cache

There are several ways to clear your Django cache:

  1. Programmatically:
python
from django.core.cache import cache

# Clear the entire cache
cache.clear()

# Clear a specific key
cache.delete('my_key')
  1. Using the Django shell:
bash
python manage.py shell
>>> from django.core.cache import cache
>>> cache.clear()
  1. Restarting the cache service (e.g., Memcached or Redis)

Real-World Example: News Website

Consider a news website that publishes articles but updates them only a few times per day. Using per-site caching would dramatically reduce server load.

Here's a complete setup example:

python
# settings.py

# Cache configuration
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': '127.0.0.1:11211',
}
}

MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]

# Cache for 20 minutes by default
CACHE_MIDDLEWARE_SECONDS = 1200
CACHE_MIDDLEWARE_KEY_PREFIX = 'news_site'

# Don't cache admin pages - this requires additional configuration
# through URL patterns or decorators

Best Practices

  1. Start with a reasonable cache timeout: Begin with a moderate cache duration (e.g., 5-15 minutes) and adjust based on your site's needs.

  2. Use a robust cache backend: For production sites, use Memcached or Redis rather than local-memory or file-based caching.

  3. Monitor cache hit rates: Track how often your cache is being utilized to optimize performance.

  4. Be aware of user-specific content: If you have user-specific content, consider using per-view caching with the vary_on_cookie parameter instead.

  5. Consider cache invalidation: Implement a strategy to invalidate cache when content changes.

Common Issues and Solutions

CSRF Protection

Per-site caching can interfere with Django's CSRF protection because the token is cached. For forms, you should either:

  1. Exclude form pages from caching
  2. Use per-view caching with appropriate settings
  3. Use JavaScript to fetch a fresh CSRF token

User-Specific Content

If parts of your page are user-specific (like a username in the corner), consider:

  1. Using AJAX to load personalized content
  2. Using template fragment caching instead
  3. Implementing a hybrid caching strategy

Summary

Django's per-site cache is a powerful tool that can dramatically improve your website's performance by caching entire pages. When properly configured, it can reduce server load, decrease response times, and handle traffic spikes more effectively.

Remember these key points:

  • Per-site caching works best for relatively static content
  • Configure your cache backend and middleware correctly
  • Be cautious with forms and user-specific content
  • Monitor and adjust your cache settings as needed

By implementing per-site caching appropriately, you can provide a much faster experience for your users while reducing the resources needed to run your Django application.

Additional Resources

Practice Exercises

  1. Set up per-site caching on a simple Django project and measure the performance difference.
  2. Create a hybrid caching strategy that uses per-site caching for anonymous users and per-view caching for authenticated users.
  3. Implement a cache invalidation mechanism that clears specific cached pages when related content is updated.

Happy caching!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)