All checks were successful
Deploy / build-deploy (push) Successful in 8m1s
152 lines
6.6 KiB
Text
152 lines
6.6 KiB
Text
# catcrafts.net Caddy site block
|
|
#
|
|
# Two upstreams: Caddy's file_server for build artifacts, and catcrafts-server
|
|
# for everything else. Point `root` at the host directory you bind-mount into
|
|
# the runner as /deploy (the "-v /path/to/webroot:/deploy" in the runner's
|
|
# config.yaml).
|
|
#
|
|
# catcrafts-server speaks PLAINTEXT HTTP/1.1 on localhost — Caddy terminates
|
|
# TLS. That is also why the backend uses Crafter.Network's ListenerHTTP1 rather
|
|
# than its HTTP/3 listener: Caddy cannot reverse_proxy to an h3 upstream.
|
|
# Do not expose port 8081 directly.
|
|
|
|
catcrafts.net {
|
|
root * /srv/catcrafts.net
|
|
encode zstd gzip
|
|
|
|
header {
|
|
Referrer-Policy "strict-origin-when-cross-origin"
|
|
X-Content-Type-Options "nosniff"
|
|
-Server
|
|
}
|
|
|
|
# ── cross-origin isolation, scoped ────────────────────────────────────
|
|
#
|
|
# The WASM runtime needs a cross-origin-isolated context (SharedArrayBuffer
|
|
# / threads), and these three headers are what provide it. They are NOT
|
|
# optional on a page that boots the module — without them it loads and the
|
|
# runtime fails.
|
|
#
|
|
# But they are scoped to the paths that actually load it, rather than applied
|
|
# site-wide. COEP: require-corp blocks every cross-origin subresource that
|
|
# does not opt in, so applying it to pages that have no wasm would constrain
|
|
# them for no benefit — and the shop's payment pages later must not inherit
|
|
# that restriction.
|
|
@isolated path /demos/* /catcrafts*.wasm /runtime.js /dom-env.js /dom-webgpu.js \
|
|
/catcrafts-head.js /files.json /variants.json /*.wgsl
|
|
header @isolated {
|
|
Cross-Origin-Opener-Policy "same-origin"
|
|
Cross-Origin-Embedder-Policy "require-corp"
|
|
Cross-Origin-Resource-Policy "same-origin"
|
|
}
|
|
# Subresources an isolated document pulls in must carry CORP themselves.
|
|
header /styles.css Cross-Origin-Resource-Policy "same-origin"
|
|
header /favicon.svg Cross-Origin-Resource-Policy "same-origin"
|
|
|
|
# ── analytics, two tiers ──────────────────────────────────────────────
|
|
#
|
|
# GoAccess reports rebuilt hourly by catcrafts-analytics.timer from the
|
|
# access logs below. Server-side only: no client-side analytics anywhere
|
|
# on the site, per the privacy policy (request logging only).
|
|
#
|
|
# /analytics/ is public and censored - visitor IPs are anonymized at
|
|
# ingest, there is no host panel, and sensitive paths never enter its DB
|
|
# (see CENSOR_RE in deploy/catcrafts-analytics). /analytics/private/ is
|
|
# the uncensored report. Generate the hash with `caddy hash-password`.
|
|
redir /analytics /analytics/ 308
|
|
redir /analytics/private /analytics/private/ 308
|
|
|
|
# The privacy policy's "view previous versions" promise. A redirect so
|
|
# the policy text stays short and survives repo or path moves - update
|
|
# the target here, never the policy wording.
|
|
redir /legal/privacy/history https://forgejo.catcrafts.net/Catcrafts/catcrafts.net/commits/branch/master/shared/interfaces/Catcrafts.Shared-Content.cppm 302
|
|
handle_path /analytics/private/* {
|
|
basic_auth {
|
|
jorijn <bcrypt-hash-here>
|
|
}
|
|
root * /var/www/analytics-private
|
|
header Cache-Control "private, no-store"
|
|
file_server
|
|
}
|
|
handle_path /analytics/* {
|
|
root * /var/www/analytics
|
|
header Cache-Control "public, max-age=600"
|
|
file_server
|
|
}
|
|
|
|
# ── build artifacts: served from disk ─────────────────────────────────
|
|
#
|
|
# file_server does sendfile, precompressed variants and range requests far
|
|
# better than the backend would. `precompressed` serves the .zst / .gz
|
|
# siblings the CI build produces, so the ~800 KB module is never recompressed
|
|
# per request. Cache-busted by the ?v=<buildId> in index.html.
|
|
@static path /catcrafts*.wasm /runtime.js /dom-env.js /dom-webgpu.js \
|
|
/catcrafts-head.js /files.json /variants.json /styles.css \
|
|
/favicon.svg /robots.txt /*.wgsl /*.jpg /posts.json /rates.json
|
|
handle @static {
|
|
header Cache-Control "public, max-age=31536000, immutable"
|
|
file_server {
|
|
precompressed zstd gzip
|
|
}
|
|
}
|
|
|
|
# ── mirrored post media ──────────────────────────────────────────────
|
|
#
|
|
# Deliberately NOT under the web root: that directory is mirrored with
|
|
# `rsync --delete` on every deploy, and this media is not always
|
|
# reproducible — if a source instance deletes a file, our copy is the only
|
|
# one left. Living on the app mount puts it physically outside the delete.
|
|
#
|
|
# Filenames are the content hash, so a changed file gets a new name and the
|
|
# immutable cache lifetime is honest.
|
|
handle_path /media/* {
|
|
root * /srv/catcrafts-app/media
|
|
header Cache-Control "public, max-age=31536000, immutable"
|
|
file_server
|
|
}
|
|
|
|
# ── everything else: server-rendered ─────────────────────────────────
|
|
#
|
|
# Pages, /feed.xml, /sitemap.xml and later /api/*. The backend sets its own
|
|
# Cache-Control and returns real status codes — a 404 for an unknown path and
|
|
# a 301 for the retired /blog URLs, which a client-side router cannot do.
|
|
handle {
|
|
reverse_proxy 127.0.0.1:8081 {
|
|
health_uri /api/healthz
|
|
}
|
|
}
|
|
|
|
# If the backend is down, fall back to the static wasm shell so the site
|
|
# degrades to a client-rendered app rather than a bare Caddy error page.
|
|
# This must be handle_errors, not handle_response: handle_response only
|
|
# fires on responses an upstream actually sent, and a backend that is not
|
|
# running refuses the connection — that is an error, which only
|
|
# handle_errors sees (verified against a dead port). The error status is
|
|
# kept, so a crawler sees the honest 502 while a human gets the working
|
|
# client-rendered app; only SSR and real 404s are lost.
|
|
handle_errors 502 503 504 {
|
|
rewrite * /index.html
|
|
header Cache-Control "no-store"
|
|
file_server
|
|
}
|
|
|
|
# ── access log ────────────────────────────────────────────────────────
|
|
#
|
|
# Feeds the GoAccess analytics (see deploy/catcrafts-analytics). Retention
|
|
# sized for that: ~15 MB/day means a roll roughly weekly; 52 compressed
|
|
# rolls ~ a year of raw logs (~6 MB each gzipped). roll_keep_for must be
|
|
# explicit — lumberjack's default silently deletes after 90 days.
|
|
log {
|
|
output file /var/log/caddy/catcrafts.net.log {
|
|
roll_size 100MiB
|
|
roll_keep 52
|
|
roll_keep_for 8760h
|
|
}
|
|
}
|
|
}
|
|
|
|
# www -> apex. DNS A/AAAA for www point at this box; Caddy obtains the
|
|
# certificate automatically. 308 to match the redirs above.
|
|
www.catcrafts.net {
|
|
redir https://catcrafts.net{uri} 308
|
|
}
|