Asset Fingerprinting
Asset fingerprinting appends a content hash to CSS and JS filenames, producing immutable URLs that CDNs and browsers can cache indefinitely. When the file content changes, the hash changes, and browsers automatically fetch the new version. It applies to accent build output and, with the same configuration, to accent serve --production .
Quick Start
- Enable fingerprinting in
config.yaml:
build: fingerprint: enabled: true
- Use the
| fingerprintfilter in your templates:
{% for css in theme.assets.css %}
<link rel="stylesheet" href="/{{ ('theme/assets/' ~ css) | fingerprint }}">
{% endfor %}
- Build your site:
accent build --output ./public --base-url https://example.com
The output will contain both the original and fingerprinted files:
public/theme/assets/css/
main.css # Original (kept for compatibility)
main.64aee5ca8b.css # Fingerprinted copy
How It Works
During accent build, after theme assets are copied and Sass/SCSS styles are compiled:
- The build walks
output/theme/assets/and matches files against the include/exclude patterns. - For each matched file, it computes the SHA-256 hash of the file contents.
- It copies the file with a fingerprinted name:
{stem}.{hash}.{ext}. - A manifest maps original paths to fingerprinted paths.
- The
| fingerprinttemplate filter resolves paths via this manifest during page rendering.
Original files are kept alongside fingerprinted copies so that external links and bookmarks continue to work.
Template Filter
Basic Usage
The | fingerprint filter takes an asset path and returns the fingerprinted equivalent:
<link rel="stylesheet" href="/{{ 'theme/assets/css/style.css' | fingerprint }}">
{# Renders: /theme/assets/css/style.a1b2c3d4e5.css #}
<script src="/{{ 'theme/assets/js/app.js' | fingerprint }}"></script>
{# Renders: /theme/assets/js/app.deadbeef01.js #}
If the path is not in the manifest (e.g., the file was excluded or fingerprinting is disabled), the filter returns the path unchanged. This means templates work correctly in both fingerprinted and non-fingerprinted builds.
Subresource Integrity (SRI)
Pass integrity=true to get an object with .url and .integrity attributes:
{% set css = 'theme/assets/css/style.css' | fingerprint(integrity=true) %}
<link rel="stylesheet"
href="/{{ css.url }}"
integrity="{{ css.integrity }}"
crossorigin="anonymous">
The .integrity value is a base64-encoded hash in SRI format (e.g., sha256-abc123...), suitable for the HTML integrity attribute. Browsers will verify the file contents match the hash before applying the stylesheet or executing the script.
Serve Mode
Serve behavior depends on the mode:
- Development serve: the
| fingerprintfilter is a no-op and returns the original path unchanged. Dev mode already sendsCache-Control: no-storeon every response, so cache busting would add nothing, and hashed URLs would churn on every asset edit under hot reload. - Production serve (
accent serve --production): whenbuild.fingerprint.enabledistrue, the server computes the fingerprint manifest in memory at startup — no files are renamed or written. The| fingerprintfilter emits the same hashed URLs as a static build, and the server resolves those hashed URLs back to the source assets on each request. Reloading the configuration (SIGHUP orPOST /_admin/reload) recomputes the manifest, so a theme update produces new hashed URLs without a restart.
In production serve, styles that go through the style pipeline (Sass compilation, minification) are hashed after processing, so SRI integrity values always match the bytes the server sends.
That guarantee holds across configuration reloads too. A production server always serves the processed form of an asset, so a reloaded config.yaml that enables development settings cannot make it send unminified CSS under a hashed URL whose integrity was computed from the minified bytes — which browsers would reject outright.
Two cache policies apply in production serve when fingerprinting is enabled:
| URL | Cache-Control |
|---|---|
Hashed URL (style.a1b2c3d4e5.css) | public, max-age=31536000, immutable |
Original URL (style.css) | public, no-cache (revalidates via ETag) |
The hashed URL is content-addressed, so the year-long immutable policy is safe: when the file changes, the URL changes. The original URL stays available for external references but revalidates on each use, so it can never pin a stale stylesheet.
Configuration
All settings are under build.fingerprint in config.yaml:
build: fingerprint: enabled: true # Default: false (opt-in) algorithm: sha256 # sha256, sha384, or sha512 length: 10 # Hex characters in the filename hash include: # Glob patterns to fingerprint - "**/*.css" - "**/*.js" exclude: # Glob patterns to skip - "**/*.map"
Settings Reference
| Setting | Default | Description |
|---|---|---|
enabled | false | Enable asset fingerprinting. Opt-in to avoid breaking existing themes. |
algorithm | sha256 | Hash algorithm: sha256, sha384, or sha512. |
length | 10 | Number of hex characters from the hash to use in filenames. |
include | ["**/*.css", "**/*.js"] | Glob patterns for files to fingerprint. |
exclude | ["**/*.map"] | Glob patterns for files to skip. |
Including Additional File Types
To fingerprint fonts and images as well:
build: fingerprint: enabled: true include: - "**/*.css" - "**/*.js" - "**/*.woff2" - "**/*.svg"
CDN Cache Headers
When fingerprinted assets are deployed behind a CDN, set long-lived cache headers:
Cache-Control: public, max-age=31536000, immutable
This tells browsers and CDNs to cache the file for one year without revalidation. Since the filename changes when the content changes, stale copies are never served.
In accent serve --production, Accent CMS sets these headers itself : hashed URLs get the immutable assets default, while the original URLs of fingerprinted assets are downgraded to a revalidating policy (see Serve Mode above). For static deployments, configure the headers in your hosting platform:
- Cloudflare Pages: Automatic for static assets
- Netlify: Add a
_headersfile with rules fortheme/assets/* - nginx:
location /theme/assets/ { expires 1y; add_header Cache-Control "public, immutable"; }
Edition Availability
Asset fingerprinting requires no license key.