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:
# 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:
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:
# 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
:
# 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:
# 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:
# 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:
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:
# 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:
- Programmatically:
from django.core.cache import cache
# Clear the entire cache
cache.clear()
# Clear a specific key
cache.delete('my_key')
- Using the Django shell:
python manage.py shell
>>> from django.core.cache import cache
>>> cache.clear()
- 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:
# 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
-
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.
-
Use a robust cache backend: For production sites, use Memcached or Redis rather than local-memory or file-based caching.
-
Monitor cache hit rates: Track how often your cache is being utilized to optimize performance.
-
Be aware of user-specific content: If you have user-specific content, consider using per-view caching with the
vary_on_cookie
parameter instead. -
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:
- Exclude form pages from caching
- Use per-view caching with appropriate settings
- 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:
- Using AJAX to load personalized content
- Using template fragment caching instead
- 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
- Set up per-site caching on a simple Django project and measure the performance difference.
- Create a hybrid caching strategy that uses per-site caching for anonymous users and per-view caching for authenticated users.
- 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! :)