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

  1. Enable fingerprinting in config.yaml:
build:
  fingerprint:
    enabled: true
  1. Use the | fingerprint filter in your templates:
{% for css in theme.assets.css %}
<link rel="stylesheet" href="/{{ ('theme/assets/' ~ css) | fingerprint }}">
{% endfor %}
  1. 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:

  1. The build walks output/theme/assets/ and matches files against the include/exclude patterns.
  2. For each matched file, it computes the SHA-256 hash of the file contents.
  3. It copies the file with a fingerprinted name: {stem}.{hash}.{ext}.
  4. A manifest maps original paths to fingerprinted paths.
  5. The | fingerprint template 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 | fingerprint filter is a no-op and returns the original path unchanged. Dev mode already sends Cache-Control: no-store on 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): when build.fingerprint.enabled is true, the server computes the fingerprint manifest in memory at startup — no files are renamed or written. The | fingerprint filter 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 or POST /_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:

URLCache-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

SettingDefaultDescription
enabledfalseEnable asset fingerprinting. Opt-in to avoid breaking existing themes.
algorithmsha256Hash algorithm: sha256, sha384, or sha512.
length10Number 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 _headers file with rules for theme/assets/*
  • nginx: location /theme/assets/ { expires 1y; add_header Cache-Control "public, immutable"; }

Edition Availability

Asset fingerprinting requires no license key.