docs/content/templates/assets.md (2929B)
1 +++
2 title = "Static assets"
3 description = "Copying, hashing, and lookup by the original name"
4 weight = 30
5 toc = true
6 +++
7
8 Everything under `static/` is copied into the output, keeping its path.
9
10 ```
11 static/
12 ├── css/main.css → /css/main.a1b2c3d4.css
13 ├── js/search.js → /js/search.e5f6a7b8.js
14 ├── images/x.png → /images/x.png
15 └── CNAME → /CNAME
16 ```
17
18 CSS and JavaScript get a content hash in the filename. Look them up by the
19 name you wrote:
20
21 ```html
22 <link rel="stylesheet" href="{{ asset("css/main.css") }}">
23 <script src="{{ asset("js/search.js") }}" defer></script>
24 ```
25
26 ## In the filename, not a query string
27
28 `main.css?v=123` has two problems. Some CDNs and proxies drop the query from
29 the cache key, so the bust does not happen. And the common shape of it — one
30 build stamp on every file — invalidates the whole site when you fix one line of
31 CSS.
32
33 A per-file content hash invalidates exactly the file that changed, and works
34 the same on every host, including hosts with no cache configuration at all.
35
36 ## Why only CSS and JS
37
38 Images are usually referenced by a literal path — from markdown, from CSS,
39 sometimes from a template you did not write. Renaming them breaks those
40 references without a way to fix them up.
41
42 And some filenames *are* the contract. `CNAME` tells GitHub Pages your domain;
43 `robots.txt` is looked for by name. `CNAME.9f8e7d6c` is a file nothing will
44 ever read.
45
46 ## Generated assets go through the same path
47
48 The highlight stylesheet is built from your themes rather than copied, but it
49 is hashed and served identically. Templates reach it as
50 `site.highlight_css`.
51
52 ## Turning it off
53
54 ```toml
55 [assets]
56 source_dir = "static"
57 fingerprint = false
58 ```
59
60 With `fingerprint = false`, files keep their names and `asset()` still works —
61 so you can flip it without editing a template.
62
63 ## asset-manifest.json
64
65 Every build writes one, at the output root, mapping logical names to the URLs
66 they were written to:
67
68 ```json
69 {
70 "css/main.css": "/css/main.a1b2c3d4.css",
71 "CNAME": "/CNAME"
72 }
73 ```
74
75 It contains every static file, not only the hashed ones, and it is written even
76 with `fingerprint = false`. Nothing in sqzass reads it back — it is there so
77 that something outside the build can answer the same question `asset()` answers
78 inside it: a service worker, a deploy script, a cache warmer.
79
80 It is safe to ignore. It is not safe to delete in a cleanup step and then wonder
81 why a tool that depended on it stopped working, which is the situation this
82 paragraph exists to prevent.
83
84 ## The search index is not hashed
85
86 `/search-en.json` and `/search-ko.json` keep fixed names. They are fetched by
87 script rather than linked from `<head>`, and their contents come from the
88 rendered pages — which are rendered after assets are resolved, so hashing them
89 would be circular. Ten minutes of a stale index costs a few search hits. See
90 [Search](@/features/search.md).