Production (Dokploy/Docker) was sending no security headers at all -
static/_headers and wrangler.toml were Cloudflare Pages/Workers
config that static-web-server never reads. Added sws.toml with the
same CSP/security headers and the two redirects from _redirects,
wired into the Dockerfile. Removed wrangler.toml, static/_headers,
static/_redirects, the wrangler devDependency/deploy script, and
related .gitignore/.dockerignore entries since Cloudflare deploy is
no longer used.
Also fixed a console error in theme.js where the Utterances
postMessage call could fire before the comments iframe navigated to
utteranc.es.
Renders a 1200x630 social card per post (satori + resvg-js, using the
site's own self-hosted fonts) instead of reusing one generic og-image.png
for every post. Wired into npm run build via a new generate:og step;
templates/page.html falls back to the per-slug image unless extra.image
is set, and non-post pages keep the generic fallback.
Adds a multi-stage Dockerfile (Node for llms.txt generation, the
official Zola image to build, static-web-server to serve) for
deploying via Dokploy/Docker instead of Cloudflare Pages. This is an
additional path, not a replacement - wrangler.toml and the Cloudflare
build.sh remain in place as an option.
Also bumps build.sh's pinned ZOLA_VERSION from 0.22.1 to 0.23.1: the
prior commit migrated every template to Tera v2 syntax (components,
spread operator, native slicing), which 0.22.1 can't parse - confirmed
by a live Cloudflare build failure on this branch. Keeping build.sh
and the Dockerfile on the same Zola version avoids maintaining two
template dialects across the two deploy paths.
Verified: `docker build` succeeds, the container serves the homepage,
a post, the youtube embed, and a proper 404; `build.sh` also runs
clean standalone (simulating the Cloudflare build) and produces a
fully populated public/.
bin/update-zola always installs the latest release, which as of
v0.23.0 rewrote the template engine to Tera v2. That broke the whole
site: removed filters (slice, concat, escape), test calls now require
named args, shortcodes are gone in favor of Tera components, and
content itself is now Tera-templated before markdown runs.
- slice(end=n) -> native [:n] slicing; concat(with=x) -> spread [...arr, x]
- escape -> escape_html; starting_with/containing -> named `pat` arg
- get_taxonomy_url `name` -> `term` (old name is deprecated)
- %+ date format no longer recognized; replaced with explicit RFC3339 pattern
- shortcodes (note/tip/warning/danger/youtube) rewritten as components;
content call sites updated to component invocation syntax
- {% include %} no longer inherits for-loop-local variables (only ambient
page/section context) - post-card.html converted to a component that
takes page/index explicitly instead of relying on implicit `page`/`loop`
- two content code blocks with literal Jinja-style {{ }} syntax (Netlify
CMS config, a Flask template snippet) wrapped in {% raw %} so Tera
doesn't try to render them, since content is now templated too
- fixed a real content corruption bug along the way: the youtube
shortcode's multi-line <iframe> tag had a conditional that could
collapse to a whitespace-only line, which prematurely closed the
HTML block under CommonMark rules and got the rest of the tag
syntax-highlighted as code instead of rendered
Verified with a clean `zola build` and `zola serve` on v0.23.1, plus a
spot-check of the rendered HTML for both callouts and the video embed.
`zola check` only reports pre-existing external link rot, unrelated to
this change.
Cloudflare Workers static assets rejects the www->apex redirect rule
with "Only relative URLs are allowed" (Pages allows absolute source
URLs for cross-host redirects, but this project deploys via wrangler's
static assets, not Pages, which doesn't). The www/apex canonicalization
needs to be handled via Cloudflare's dashboard Redirect Rules instead.
15 screenshots in the ScriptCTF writeup had empty alt text. Also removed
two <img> references in the semver post pointing at files that never
existed in the repo (leftover from the old Netlify CMS media setup).
[[extra.menu]] silently swallowed ~20 later extra.* keys (posts_page_title,
search_placeholder, show_rss_link, notfound_*, etc.) into the last menu
entry/tag_descriptions table, since TOML absorbs bare keys into the last
open table until a new header appears. show_rss_link was the only visibly
broken one (RSS link missing from homepage); the rest were masked by
matching template defaults.
Also replaced Tera's default() filter with explicit if/else where values
feed into JSON-LD, since default()'s fallback gets escaped in a way `safe`
can't undo. Added author/dateModified to BlogPosting list entries.
- new paper/ink palette with single red accent, replacing catppuccin
- self-hosted IBM Plex Serif (display) + Source Serif 4 (body), drop Outfit
- rebuilt homepage masthead, numbered post index, navbar with active states
- switch syntax highlighting + utterances to gruvbox, synced via postMessage
- remove terminal/404 easter egg chrome, clean up remaining inline styles
- fix stray syntax.css link causing a 404