CDN Integration
Overview
CDN integration lets you front Accent CMS with a content delivery network (Cloudflare, Fastly, CloudFront, Bunny, …) and adds three capabilities on top of the standard HTTP cache headers:
- Asset URL rewriting — point asset references at your CDN origin from
templates (
cdn_url()) and from rendered markdown images (automatic). - Font privacy proxy — serve Google Fonts from your own origin so visitor browsers never contact Google, avoiding the IP-address leak that European courts have ruled a GDPR violation.
- Cache purge coordination — notify the CDN to drop stale pages when content changes (outbound), and let the CDN invalidate the local page cache (inbound).
CDN integration is a Pro feature. The helper functions are always available
so themes stay portable, but enabling cdn.enabled for accent serve --production requires a Pro license. Static accent build is always free.
Configuration
Add a cdn: section to config.yaml:
cdn: enabled: true base_url: "https://cdn.example.com" font_proxy: enabled: true families: - "Inter:wght@400;700" cache_dir: ./.font-cache purge: webhook_secret: "${CDN_PURGE_SECRET}" on_content_change: true cloudflare: zone_id: "${CF_ZONE_ID}" api_token: "${CF_API_TOKEN}" # Or, for any other CDN, a generic webhook: # webhook: # url: "https://api.fastly.com/purge/..." # method: POST # headers: # Fastly-Key: "${FASTLY_API_TOKEN}"
Secret-bearing values (webhook_secret, the Cloudflare token, generic webhook
headers) support ${VAR} and ${VAR:-default} environment-variable
placeholders, expanded at config load — keep secrets out of version control by
injecting them from the environment.
Asset URL rewriting
In templates
Use cdn_url() for any asset you want served from the CDN:
<link rel="stylesheet" href="{{ cdn_url('/assets/css/main.css') }}">
<script src="{{ cdn_url('/assets/js/app.js') }}"></script>
With cdn.enabled: true and base_url: "https://cdn.example.com", this emits
https://cdn.example.com/assets/css/main.css. With CDN disabled it returns the
path unchanged, so the same templates work on every edition. The rewrite happens
identically in accent serve and accent build, so static output already
points at the CDN.
In markdown content
Image URLs in markdown are rewritten automatically when CDN is enabled. A local
image like  becomes
https://cdn.example.com/media/hero.webp in the rendered HTML — no template
changes needed. Only paths beginning with one of the configured
rewrite_prefixes (by default /media/, /content-media/, /assets/,
/theme/assets/) are rewritten; external and unrelated URLs pass through
untouched.
Font privacy proxy
A direct Google Fonts <link> makes every visitor’s browser request
fonts.googleapis.com and fonts.gstatic.com, sending their IP, User-Agent,
and Referer to Google. The font proxy eliminates this: Accent CMS fetches the
CSS and font files once, caches them locally, rewrites the @font-face url()s
to same-origin /_fonts/ paths, and serves everything from your own domain.
Link fonts through font_css_url():
<link rel="stylesheet" href="{{ font_css_url('Inter:wght@400;700') }}">
With the proxy enabled this renders a same-origin path
(/_fonts/css/inter-wght-400-700.css); with it disabled it falls back to a
direct Google Fonts URL. List every family you use under
cdn.font_proxy.families so the static accent build can pre-download them into
the output directory.
Cache purge
Outbound (Accent CMS → CDN)
With purge.on_content_change: true, a content change in accent serve triggers
a best-effort purge so the CDN drops its stale copies. Configure either the
first-class cloudflare integration (zone purge) or a webhook for any other
provider.
Inbound (CDN → Accent CMS)
A CDN (or any trusted orchestrator) can invalidate Accent CMS’s local page cache
by POSTing to /_webhooks/cdn/purge with an X-Accent-Signature HMAC-SHA256
header computed over the request body using purge.webhook_secret:
{ "urls": ["/blog/my-post/"] }
Send { "purge_all": true } to clear the entire cache. The endpoint is only
mounted when cdn.enabled is set and webhook_secret resolves to a real value
(an empty or unexpanded ${VAR} placeholder is refused, matching the git push
webhook).
Reference
cdn_url()andfont_css_url()— template helpers- HTTP cache headers — the per-edition caching foundation CDN builds on