Browse files

Added documentation for versions.

  • Loading branch information...
1 parent acefe50 commit a79aa29b80fd014bcb22e13958ffbdf57c4486c0 @miracle2k committed Mar 9, 2012
@@ -29,7 +29,9 @@ arguments:
applied in the order in which they are given.
* ``output`` - Name/path of the output file. All source files will be merged
- and the result stored at this location.
+ and the result stored at this location. A ``%(version)s`` placeholder is
+ supported here, which will be replaced with the version of the file. See
+ :doc:`/expiring`.
Nested bundles
@@ -23,12 +23,16 @@ in your project's global settings.
.. autodata:: ASSETS_DEBUG
-.. autodata:: ASSETS_UPDATER
+.. autodata:: ASSETS_AUTO_BUILD
-.. _django-setting-expire:
+.. autodata:: ASSETS_URL_EXPIRE
+ :noindex:
+.. autodata:: ASSETS_VERSIONS
+ :noindex:
-.. autodata:: ASSETS_EXPIRE
+.. autodata:: ASSETS_MANIFEST
.. autodata:: ASSETS_CACHE
@@ -61,11 +61,13 @@ The environment supports the following configuration options:
.. autoattribute:: webassets.env.Environment.debug
-.. autoattribute:: webassets.env.Environment.updater
+.. autoattribute:: webassets.env.Environment.auto_build
-.. _environment-setting-expire:
+.. autoattribute:: webassets.env.Environment.url_expire
-.. autoattribute:: webassets.env.Environment.expire
+.. autoattribute:: webassets.env.Environment.versions
+.. autoattribute:: webassets.env.Environment.manifest
.. autoattribute:: webassets.env.Environment.cache
@@ -0,0 +1,189 @@
+.. _expiry:
+URL Expiry (cache busting)
+For beginners
+You are using ``webassets`` because you care about the performance of your
+site. For the same reason, you have configured your web server to send out
+your media files with a so called *far future expires* header: Your web server
+sets the ``Expires`` header to some date many years in the future. Your user's
+browser will never spend any time trying to retrieve an updated version.
+.. note::
+ Of course, the user's browser will already use the ``Etag`` and
+ ``Last-Modified/If-Modified-Since`` to avoid downloading content it has
+ already cached, and if your web server isn't misconfigured entirely, this
+ will work. The point of *far future expires* is to get rid of **even**
+ those requests which would return only a ``304 Not Modified`` response.
+What if you actually deploy an update to your site? Now you need to convince
+the browser to download new versions of your assets after all, but you have
+just told it not to bother to check for new versions. You work around this by
+*modifying the URL with which the asset is included*. There are two distinct
+ways to so:
+1) Append a version identifier as a querystring::
+2) Add a version identifier to the actual filename::
+How webassets helps you do this is explained in the sections below.
+.. note::
+ Even if you are not using *far future expires* headers, you might still find
+ ``webassets`` expiry features useful to navigate around any funny browser
+ caching behaviour that might require a ``Shift``-reload.
+What is the version of a file
+To expire an URL, it is modified with a version identifier. What is this
+identifier? By default, ``webassets`` will create an MD5-hash of the file
+contents, and use the first few characters as the file version. ``webassets``
+also allows you to use the *last modified* timestamp of the file. You can
+configure this via the ``versions`` option::
+ env = Environment(...)
+ env.versions = 'hash' # the default
+ env.versions = 'hash:32' # use the full md5 hash
+ env.versions = 'timestamp' # use the last modified timestamp
+It is generally recommended that you use a hash as the version, since it will
+remain the same as long as the content does not change, regardless of any
+filesystem metadata, which can change for any number of reasons.
+Expire using a querystring
+``webassets`` will automatically add the version as a querystring to the urls
+it generates, by virtue of the ``url_expire`` option defaulting to ``True``.
+If you want to be explicit::
+ env = Environment(...)
+ env.url_expire = True
+There is nothing else you need to do here. The URLs that are generated might
+look like this::
+ /media/print.css?acefe50
+However, while the default, expiring with a querystring is not be the best
+Expire using the filename
+Adding the version as a querystring has two problems. First, it may not always
+be a browser that implements caching through which we need to bust. It is said
+that certain (possibly older) proxies do ignore the querystring with respect
+to their caching behavior.
+Second, in certain more complex deployment scenarios, where you have multiple
+frontend and/or multiple backend servers, an upgrade is anything but
+instantaneous. You need to be able to serve both the old and the new version
+of your assets at the same time. See for example how this affects you `when
+using Google App Engine <>`_.
+To expire using the filename, you add a ``%(version)s`` placeholder to your
+bundle output target::
+ bundle = Bundle(..., output='screen.%(version)s.css')
+The URLs that are generated might look like this::
+ /media/screen.acefe50.css
+.. note::
+ ``webassets`` will use this modified filename for the actual output files
+ it writes to disk, as opposed to just modifying the URL it generates. You
+ do not have to configure your web server to do any rewriting.
+About manifests
+.. note::
+ This is mostly an advanced feature, and you might not have to bother with
+ it at all.
+``webassets`` supports Environment-wide *manifests*. A manifest remembers the
+current version of every bundle. What is this good for?
+1) Speed. Calculating a hash can be expensive. Even if you are using
+ timestamp-based versions, that still means a stat-request to your disk.
+ .. note::
+ Note that even without a manifest, ``webassets`` will cache the version
+ in memory. It will only need to be calculated once per process. However,
+ if you have *many* bundles, and a very busy site, a manifest will allow
+ you to both skip calculating the version (e.g. creating a hash), as well
+ as read the versions of all bundles into memory at once.
+ .. note::
+ If you are using automatic building, all of this is mostly not true. In
+ order to determine whether a rebuild is required, ``webassets`` will need
+ to check the timestamps of all files involved in any case. It goes
+ without saying that using automatic building on a production site is a
+ convenience feature for small sites, and at odds with counting paper
+ clips in the form of filesystem ``stat`` calls.
+2) Making it possible to know the version in the first place.
+ Depending on your configuration and deployment, consider that it might not
+ actually be possible for ``webassets`` to know what the version is.
+ If you are using a hash-based version, and your bundle's output target has
+ a placeholder, there is no way to know what the version is, *unless* is
+ has been written to a manifest during the build process.
+ The timestamp-based versioning mechanism can actually look at the source
+ files to determine the version. But, in more complex deployments, the source
+ files might not actually be available to read - they might be on a
+ completely different server altogether.
+ A manifest allows version information to be persisted.
+In practice, by default the version information will be written to the cache.
+You can explicitly request this behaviour be setting the ``manifest`` option::
+ env = Environment(...)
+ env.manifest = 'cache'
+In a simple setup, where you are separately building on your local machine
+during development, and building on the web server for production (maybe via
+the automatic building feature, enabled by default), this is exactly would
+you want. Don't worry about it.
+There is a specific deployment scenario where you want to prebuild your bundles
+locally, and for either of the two reasons above want to include the version
+data pre-made when you deploy your app to the web server. In such a case, it
+is not helpful to have the versions stored in the cache. Instead, ``webassets``
+provides a manifest type that writes all information to a single file::
+ env = Environment(...)
+ env.manifest = 'file'
+ env.manifest = 'file:/tmp/' # explict filename
+You can then just copy this one file to the web server, and ``webassets``
+will know all about the versions without having to consult the media files.
+.. note::
+ The file is a pickled dict.
@@ -6,10 +6,7 @@ FAQ
Is there a cache-busting feature?
-Yes! It's turned on by default. See the
-:ref:`Environment.expire <environment-setting-expire>`
-option (or :ref:`ASSETS_EXPIRE <django-setting-expire>` if using
+Yes! See :doc:`/expiring`.
Relative URLs in my CSS code break if the merged asset is written to a different location than the source files. How do I fix this?
@@ -28,20 +25,15 @@ See :doc:`css_compilers` for how this is best done.
Is Google App Engine supported?
-It generally works, though further improvements are planned. Due to the
-way Google App Engine works (static files are stored on separate servers),
-you need to build your assets locally, possibly using one of the management
-commands provided for your preferred framework, and then deploy them.
+Yes. Due to the way Google App Engine works (static files are stored on
+separate servers), you need to build your assets locally, possibly using one
+of the management commands provided for your preferred framework, and then
+deploy them.
-In production mode, you therefore want to disable the
-``Environment.updater``/``ASSETS_UPDATER`` setting.
+In production mode, you need to disable the ``Environment.auto_build`` setting.
-Further, you currently need to disable
-``Environment.expire``/``ASSETS_EXPIRE`` for webassets to work on Google's
-servers. This means you will not get url expiration functionality. This will
-be fixed in the future. In the meantime, you can write some custom code
-to provide the feature. See `this gist <>`_
-for an example.
+For URL expiry functionality, you need to use a manifest that holds version
+information. See :doc:`/expiring`.
There is a barebone Google App Engine example in the
`examples/appengine/ <>`_
@@ -52,7 +52,7 @@ you do this depends a bit on how your site is rendered.
.. code-block:: python
>>> my_env['js_all'].urls()
- ('../static/media/gen/packed.js',)
+ ('../static/media/gen/packed.js?9ae572c',)
This will always work. You can call your bundle's ``urls()`` method, which
will automatically merge and compress the source files, and return the
@@ -95,4 +95,4 @@ Further Reading
- /faq
+ /faq
@@ -25,9 +25,10 @@ of framework used:
+ expiring
- upgrading
+ upgrading
@@ -4,9 +4,9 @@
env = Environment(path.join(path.dirname(__file__), 'static'), '/stylesheets')
# App Engine doesn't support automatic rebuilding.
-env.updater = False
-# URL expiry not currently supported on App Engine
-env.expire = False
+env.auto_build = False
+# This file needs to be shipped with your code.
+env.manifest = 'file'
bundle = Bundle('in.css', filters="cssmin", output="out.css")
@@ -23,8 +23,10 @@ def __init__(self, object, append=None):
ASSETS_DEBUG = Environment.debug
ASSETS_CACHE = Environment.cache
-ASSETS_UPDATER = Environment.updater
-ASSETS_EXPIRE = Environment.expire
+ASSETS_AUTO_BUILD = Environment.auto_build
+ASSETS_URL_EXPIRE = Environment.url_expire
+ASSETS_MANIFEST = Environment.manifest
+ASSETS_VERSIONS = Environment.versions
ASSETS_URL = docwrap(Environment.url, """\n\nBy default, ``STATIC_URL``
will be used for this, or the older ``MEDIA_URL`` setting.""")
ASSETS_ROOT = docwrap(, """\n\nBy default,
@@ -241,8 +241,6 @@ def _get_cache(self):
*custom path*
Use the given directory as the cache directory.
- Note: Currently, the cache is never used while in production mode.
def _set_auto_build(self, value):
@@ -325,7 +323,8 @@ def _get_versions(self):
A bundle's version is what is appended to URLs when the
``url_expire`` option is enabled, and the version can be part
- of a Bundle's output filename by use of the %(version)s placeholder.
+ of a Bundle's output filename by use of the ``%(version)s``
+ placeholder.
Valid values are:
@@ -384,7 +383,7 @@ def _get_url_expire(self):
webassets will have their version (see ``Environment.versions``)
appended as a querystring.
- An alternative approach would be to use the %(version)s
+ An alternative approach would be to use the ``%(version)s``
placeholder in the bundle output file.
By default, this option is enabled.

0 comments on commit a79aa29

Please sign in to comment.