Introduction
Why Caching
In production environments, caching the static contents is a well known practice to improve the user experiences and minimise resource utilisation. In most cases caching for static contents implemented in multiple levels from application servers to CDNs to browser.
A good caching policy always helps to deliver up to date contents with great user experience. However the caching the static contents also introduces some limitations like reflecting the recent modifications done to the static contents. So, any good cache policy cannot be the best cache policy at all.
Downsides of Caching
In a situation where there are new contents to be served but previous caches still serving the older contents, and you cannot wait for your caching policies to invalidate your old contents timely, forced cache invalidation is necessary. Therefore, we can either go to each point where the older contents have been cached and invalidate them explicitly. But, it could be time-consuming or impossible task for most cases.
Cache Busting
Instead, we can take another approach where we invalidate all the caches by modifying the cache key causing cache miss at every point. This forces the end consumer of the content to retrieve from the original source which is up to date in real time. This approach is commonly know as cache busting.
Cache busting is the known solution when there is a need for explicitly reflect the changes done in static contents in real time. For that, there are few methods.
- File Name Versioning
- File Name Hashing
- File Path Versioning
- Query Parameters
Managing Static Contents in Django
Django framework shipped with a decent mechanism to manage and serve the static files defined in each of the apps inside a Django project.
When the project is ready to be served in production mode collectstatic
command collects all the static files defined in each configured apps into a folder path defined with STATIC_ROOT
setting. Then the static contents will be served from the defined STATIC_ROOT
path.
As we understood in the introduction, these static files might get cached at certain levels based on the caching policies defined in application servers, proxy servers, CDNs, and browsers.
The default configuration on Django does not provide any options to bust caching when there are new changes in static files that are collected into the STATIC_ROOT
. Thus, a production deployment of a Django project with default static content configurations may still serve the outdated content to the end consumers.
Django ManifestStaticFilesStorage
As we know the default configuration of static content management does not support options to bust caches if there is an updated version of static content available. Because the default implementation of static file collection is handled by StaticFilesStorage
However, ManifestStaticFilesStorage
a subclass of StaticFilesStorage
adds a post-processing functionality that appends an MD5 hash to each of the collected file names which is a cache-busting technique discussed in the previous section.
To enable ManifestStaticFilesStorage
as the active static file storage handler for a Django project, we must explicitly set the STATICFILES_STORAGE
setting as follows.
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
When debug mode enabled with
DEBUG = True
setting, this configuration will not post process the static files with hashed oncollectstatic
command.Also these hashed files are not served through the development server provided by Django framework.
Common Caveat
When ManifestStaticFilesStorage
is configured as the Static Files Storage, post-processing recursively identifies any referring paths in processing file content (such as CSS file imports, and image path references in a CSS file). Then the post-processing task attempt to rename the identified files by appending the MD5 hash of the respective file.
If there is a path reference that doesn't exist, the static file collection will stop processing while raising an error. To resolve this issue file path references should be corrected, and the files should be available and accessible to post-process by the task.
Conclusion
Although ManifestStaticFilesStorage
can be used as a standard way to implement a cache-busting mechanism, it is supported for other use cases as well. For example, it is useful when you want to serve multiple versions of static content (eg: CSS files) to different clients.
References