So far in this hour we have discussed how to implement caching on your own website. Web pages are also cached upstream from your website by ISPs, proxies, and even web browsers. Upstream caching provides a major boost to the Internet's efficiency, but it can also pose a couple of problems and security holes. For example, a home page that contains personal data about a user may be cached. A subsequent request to that home page would display that user's information in another user's browser.
The HTTP protocol solves these types of problems using Vary and Cache-Control headers. They allow websites to define some behavior and access requirements before cached pages are distributed. The following sections discuss how to implement these headers in your view functions.
The Vary header allows you to define headers that an upstream cache engine checks when building its cache key. Then the cached page is used only if the values of headers in the Vary header of the request match those in the database.
The Vary header can be set in several different ways in the view function. The simplest way is to set the header manually in the HttpResponse object using the following syntax:
def myView(request): . . . response = HttpResponse() response['Vary'] = 'User-Agent'
Setting the Vary header manually in this way can potentially overwrite items that are already there. Django provides the django.views.decorators.vary.vary_on_headers() decorator function so that you can easily add headers to the Vary header for the view function.
The vary_on_headers() decorator function adds headers to the Vary header instead of overwriting headers that are already there. The vary_on_headers() decorator function can accept multiple headers as arguments. For example, the following code adds both the User-Agent and Content-Language headers to the Vary header:
from django.views.decorators import vary_on_headers @vary_on_headers('User-Agent', 'Content-Language') def myView(request): . . .
Another useful function to modify the Vary header is the django.utils.cache.patch_vary_headers(response, [headers]) function. The patch_vary_headers() function requires a response object as the first argument and a list of headers as the second. All headers listed in the second argument are added to the Vary header of the response object. For example, the following code adds the User-Agent and Content-Language headers to the Vary header inside the view function:
from django.utils.cache import patch_vary_headers def myView(request): . . . response = HttpResponse() patch_vary_headers(response, ['User-Agent', 'Content-Language'])
One of the biggest advantages of using the patch_vary_headers() function is that you can selectively set which headers to add using code inside the view function. For example, you might want to add the Cookie header only if your view function actually sets a cookie.
By the Way
The values that get passed to vary_on_headers() and patch_vary_headers() are not case-sensitive. For example, the header user-agent is the same as User-Agent.
One of the most common headers that you will want to add to the Vary header is the Cookie header. For that reason, Django has added the django.views.decorators.vary_on_cookie() decorator function to add just the Cookie header to the Vary header. The vary_on_cookie() decorator function does not accept any parameters and simply adds the Cookie header to Vary:
from django.views.decorators import vary_on_cookie @vary_on_cookie def myView(request): . . .
One of the biggest problems with caching is keeping data that should remain private, private. Users basically use two types of caches—the private cache stored in the user's web browser, and the public cache stored by ISPs or other upstream caches. Private data, such as credit card numbers and account numbers, should only be stored in the private cache.
HTTP handles the issue of keeping data private using the Cache-Control header. The Cache-Control header allows you to define directives that caching engines will use to determine if data is public or private and if it should even be cached.
The following are the currently valid directives for the Cache-Control header:
public=True
private=True
no_cache=True
no_store=True
no_transform=True
must_revalidate=True
proxy_revalidate=True
max_age=num_seconds
s_maxage=num_seconds
Django provides the django.views.decorators.cache.cache_control() decorator function to configure the directives in the Cache-Control header. The cache_control() decorator function accepts any valid Cache-Control directive as an argument. For example, the following code sets the private and max_age directives in the Cache-Control header for a view function:
from django.views.decorators.cache import cache_control @ cache_control(private=True, max_age=600) def myView(request): . . .
By the Way
The max_age directive in the Cache-Control header is set to the value of CACHE_MIDDLEWARE_SECONDS if it is specified in the settings.py file. The value you add to the max_age directive in the cache_control() decorator function takes precedence.