You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Send HSTS, nosniff, and Referrer-Policy From the Blog Site Block #497
The blog site block in deploy/Caddyfile sends no Strict-Transport-Security, X-Content-Type-Options, or Referrer-Policy, while the family block sets all three, plus a Content-Security-Policy. The host side found this while measuring blog.insanegenius.com through Cloudflare (ptr727/HomeAutomation-Config#580), and a live read confirms it: zero of the four headers on a blog response, four on a family response.
No commit, issue, or doc records a reason to leave them out. The family block gained them with #271, and the older blog block never got the same set.
Why it matters
HSTS. Without it, a reader who follows an http:// link takes the cleartext redirect on every visit, and an on-path attacker can intercept that hop. With it, the browser skips the cleartext hop after the first HTTPS visit.
The host-side alternative is worse. Edge HSTS at Cloudflare binds every name in the zone and does not cover direct access to the origin, so the maintainer keeps it off. The header belongs in the bundle.
Change
Add the three headers to the blog block's baseline, with the family block's values:
Strict-Transport-Security "max-age=31536000", with no includeSubDomains and no preload, matching the family block
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
Verify on the local staging mirror that every class carries them, redirects and the 404 included, and that check-live-urls.sh passes. A media file served as nosniff must still carry the right Content-Type, which check_media asserts.
A Content-Security-Policy for the blog is a separate decision and stays out of this issue. Posts embed third-party content, so a policy needs a survey of what the posts load first.
Scope widened: make the two sites consistent in posture. The maintainer's instruction, relayed by the HomeAutomation-Config session: fix this issue, and in the same pass check every config and setting for consistency between the blog and the family site. Per the maintainer's choice in the session that filed this, the work runs in a fresh session from handoff #493.
Survey the differences rather than assuming them. Each one ends as "aligned in this PR" or "kept, because ", and the PR body lists every one. The host side named these starting points:
Security headers. HSTS, X-Content-Type-Options, and Referrer-Policy are on the family site and absent on the blog, which is this issue's original scope. The family site has a Content-Security-Policy and the blog has none. It stays out pending an embed survey, but deploy/README.md states that difference and its reason rather than leaving it silent.
Baseline header handling. The family block sets its headers in one header {} block, while the blog block sets X-Blog-Env alone and layers the cache classes in a route. Check that X-Robots-Tag and X-Blog-Release behave the same on both, redirects and error pages included, since the family block sets both explicitly.
Everything else the two sites treat differently: the Caddyfile, the container environment (TRUSTED_PROXIES, SITE_ENV, the robots defaults), the check scripts, and the deploy docs.
Done means the PR is merged and released to production, and the HomeAutomation-Config session is told, so the host side can re-measure both names through the proxy. The host side reports that the zone settings for both zones are already identical.
Consistency audit, 2026-10-24. This was read-only. It covers deploy/Caddyfile, deploy/make-release.sh, checks/check-live-urls.sh, deploy/README.md, the host compose files for both mirrors and the VPS, and the headers each production site serves per class through Cloudflare (release 1.0.259). Proposed dispositions are below, and the PR body carries the final ones.
Differences
#
Area
Blog
Family
Proposed
1
Strict-Transport-Security
absent
max-age=31536000
Align: add it to the blog, same value, with no includeSubDomains or preload
2
X-Content-Type-Options
absent
nosniff
Align
3
Referrer-Policy
absent
strict-origin-when-cross-origin
Align
4
Content-Security-Policy
absent
set, with script-src 'self' and Google Fonts
Keep, and document why in deploy/README.md. Posts embed third-party content, which needs a survey before any policy
Align: the family images take the blog's one-hour image class, since the reason is the same
6
SVG
no class, so no-cache, or an hour under /media/
public, max-age=604800 (crest.svg, favicon.svg)
Align to one hour.#271 gives no reason for a week, and the blog's reason for an hour, bounding how long a replaced image stays in any copy, applies equally
7
CSS and JS
/assets/, fingerprinted, a year immutable
site.css and site.js, not fingerprinted, no-cache
Keep: an unfingerprinted name has to revalidate. Document the reason in "Cache lifetimes", which says only "its own, shorter list" today
8
Header layout in the Caddyfile
three top-level header lines plus the route cache classes
one header {} block plus matchers
Keep the layout. The served result is the same on every class measured (page, redirect, 404, image, robots.txt): X-Blog-Env, X-Robots-Tag, and X-Blog-Release reach all of them on both sites. Put the new blog security headers in one block beside the others
Same on both, no change
Environment and robots variables. One container serves both sites. Each environment sets SITE_ROBOTS and FAMILY_SITE_ROBOTS to the same value: noindex, nofollow on both mirrors, and production index, follow. Two variables exist by design, per deploy/README.md, so the sites can reach production on different schedules. TRUSTED_PROXIES is shared.
Error handling. Both answer a stray path with status 404. The blog serves /404.html, and the family site serves its own page, by design for a single-page site.
Redirect codes differ by purpose, not by drift. The blog's legacy redirects are permanent 301s. The family site's language choice is a 302, since it depends on the browser, and /en and /af gain their slash with a 308.
Vary: Accept-Language appears only on the family root, because only it redirects by language.
robots.txt and the sitemap. Each site serves both, and each robots.txt names its own sitemap.
Release check coverage. The blog runs the full URL contract. The family site checks its three pages for title, X-Blog-Env, and X-Blog-Release, which fits a one-page site, and make-release.sh refuses a bundle missing a family page the Caddyfile serves.
Logging. Both use log { output stdout format console }.
Host side. Both production names are proxied with identical zone settings, and both staging names are DNS-only.
Verify in the PR
On the local staging mirror, read every class on both sites and confirm the table above holds after the change, then run check-live-urls.sh in full. After release, tell the HomeAutomation-Config session so it can re-measure both names through the proxy.
When the release is live on production, tell the HomeAutomation-Config session so it can re-measure both names through the proxy. That session confirmed the audit dispositions need no host change. If it has closed, comment the release on ptr727/HomeAutomation-Config#580 instead.
Activity
ptr727 commentedon Oct 24, 2026
Scope widened: make the two sites consistent in posture. The maintainer's instruction, relayed by the HomeAutomation-Config session: fix this issue, and in the same pass check every config and setting for consistency between the blog and the family site. Per the maintainer's choice in the session that filed this, the work runs in a fresh session from handoff #493.
Survey the differences rather than assuming them. Each one ends as "aligned in this PR" or "kept, because ", and the PR body lists every one. The host side named these starting points:
X-Content-Type-Options, andReferrer-Policyare on the family site and absent on the blog, which is this issue's original scope. The family site has aContent-Security-Policyand the blog has none. It stays out pending an embed survey, butdeploy/README.mdstates that difference and its reason rather than leaving it silent.header {}block, while the blog block setsX-Blog-Envalone and layers the cache classes in aroute. Check thatX-Robots-TagandX-Blog-Releasebehave the same on both, redirects and error pages included, since the family block sets both explicitly.@svg(a week) and@codeclasses predate the Name a Cache Lifetime on Every Response Ahead of an Edge Proxy #492 class table. Either both sites express the same classes the same way, ordeploy/README.mdsays why they differ.TRUSTED_PROXIES,SITE_ENV, the robots defaults), the check scripts, and the deploy docs.Done means the PR is merged and released to production, and the HomeAutomation-Config session is told, so the host side can re-measure both names through the proxy. The host side reports that the zone settings for both zones are already identical.
ptr727 commentedon Oct 24, 2026
Consistency audit, 2026-10-24. This was read-only. It covers
deploy/Caddyfile,deploy/make-release.sh,checks/check-live-urls.sh,deploy/README.md, the host compose files for both mirrors and the VPS, and the headers each production site serves per class through Cloudflare (release 1.0.259). Proposed dispositions are below, and the PR body carries the final ones.Differences
Strict-Transport-Securitymax-age=31536000includeSubDomainsorpreloadX-Content-Type-OptionsnosniffReferrer-Policystrict-origin-when-cross-originContent-Security-Policyscript-src 'self'and Google Fontsdeploy/README.md. Posts embed third-party content, which needs a survey before any policyfavicon.ico,apple-touch-icon.png,og-image.png)public, max-age=3600no-cache, through@pageno-cache, or an hour under/media/public, max-age=604800(crest.svg,favicon.svg)/assets/, fingerprinted, a year immutablesite.cssandsite.js, not fingerprinted,no-cacheheaderlines plus theroutecache classesheader {}block plus matchersrobots.txt):X-Blog-Env,X-Robots-Tag, andX-Blog-Releasereach all of them on both sites. Put the new blog security headers in one block beside the othersSame on both, no change
SITE_ROBOTSandFAMILY_SITE_ROBOTSto the same value:noindex, nofollowon both mirrors, and productionindex, follow. Two variables exist by design, perdeploy/README.md, so the sites can reach production on different schedules.TRUSTED_PROXIESis shared.Cache-Controlon pages, redirects, and the error page isno-cacheon both. Both serve precompressedbrandgzip, the error pages included since Defeat the Edge Cache in the Media Check and Name the Zone Settings #496.404. The blog serves/404.html, and the family site serves its own page, by design for a single-page site.301s. The family site's language choice is a302, since it depends on the browser, and/enand/afgain their slash with a308.Vary: Accept-Languageappears only on the family root, because only it redirects by language.robots.txtand the sitemap. Each site serves both, and eachrobots.txtnames its own sitemap.X-Blog-Env, andX-Blog-Release, which fits a one-page site, andmake-release.shrefuses a bundle missing a family page the Caddyfile serves.log { output stdout format console }.Verify in the PR
On the local staging mirror, read every class on both sites and confirm the table above holds after the change, then run
check-live-urls.shin full. After release, tell the HomeAutomation-Config session so it can re-measure both names through the proxy.ptr727 commentedon Oct 24, 2026
When the release is live on production, tell the HomeAutomation-Config session so it can re-measure both names through the proxy. That session confirmed the audit dispositions need no host change. If it has closed, comment the release on ptr727/HomeAutomation-Config#580 instead.