zola asset integrity
As a diligent Over Engineer, a while ago I added two things to how Zola generates links to assets:
integrityattributes to CSS<link>s, which means the browser will reject CSS with the wrong hash?h=0123456789abcdeffaux-GETparams to the asset URLs themselves, so that I can set long HTTP cache times on them
This looks like this in the Zola template:
<link
integrity="sha384-{{ get_hash(path="css/some.css", sha_type=384, base64=true) | safe }}"
rel="stylesheet" href="{{ get_url(cachebust=true, path="css/some.css") }}">
which produces output like this in the generated HTML:
<link
integrity="sha384-6d001da919b965dc3a4672b9d7ddce374d165452a2285f2753988842092ea6b9946645375cff3ede89a991c9698bfcea"
rel="stylesheet" href="/css/some.css?h=0123456789abcdef">
So, now, if a malicious TLA MITMs you or compromises the server serving static assets, the browser will reject them (from the console log):
[Error] Cannot load stylesheet http://127.0.0.1:1111/css/main.css?h=0123456789abcdef. Failed integrity metadata check. Content length: 7715, Expected content length:
7715, Expected metadata: sha384-6d001da919b965dc3a4672b9d7ddce374d165452a2285f2753988842092ea6b9946645375cff3ede89a991c9698bfcea
In other news, Zola has a serve
command
that runs a little dev server and reloads if any of the files change:
❯ mise run serve
[serve] $ zola serve --interface 0.0.0.0 --base-url / --port=${PORT:-1111} --drafts
Building site...
Checking all internal links with anchors.
> Successfully checked 0 internal link(s) with anchors.
-> Creating 1 pages (0 orphan) and 0 sections
Done in 7ms.
Web server is available at / (bound to 0.0.0.0:1111)
Listening for changes in /home/user/project/{config.toml,content,static,templates}
Press Ctrl+C to stop
Change detected @ 2026-01-12 15:03:46
-> Static file changed /home/user/project/static/css/some.css
Done in 2ms.
You can see at the bottom that it reloaded the CSS file that had changed.
Unfortunately, Zola doesn't tie these two features
together and if you use
integrity and edit the assets while zola is running, then it will
start serving the new asset with the old HTML, and then the browser
will quite reasonably tell you to fuck off and stop loading the CSS.
Excitingly, browsers don't really show that this is happening anywhere
except in the console log, and if you have multiple style sheets and
edited only one, the behaviour you will see is:
- everything is fine
- edit a CSS file
- reload page
- styles are all fucked up, and if you're like me, assume it's because your edit was terrible
- think that's weird, let's blame caching or something
- restart zola
- everything is fine
- undo change to CSS file
- styles are all fucked up, and if you're like me, assume it's because your edit was terrible
- etc
On the bug, user legoktm provides a pretty good workaround, that just removes the integrity attribute entirely when in local dev serving mode:
Firstly, add a new macro in templates/macros.html:
{% macro sri(path) -%}
{% if config.mode != "serve" -%}
integrity="sha384-{{ get_hash(path=path, sha_type=384, base64=true) | safe }}"
{%- endif %}
{%- endmacro %}
Then at the top of templates/base.html (or wherever your <link> tags are done):
{% import "macros.html" as macros %}
and wherever you actually write out the <link> in the templates, do this instead:
<link rel="stylesheet" href="{{ get_url(path="static/some.css", cachebust=true) }}"
{{-macros::sri(path="static/some.css")}} />
(note the - before the m in macros - it tells the
Tera template language to strip
whitespace. If you forget that, in serve mode the macros will be
reduced to a newline instead of nothing).