Skip to content

Send HSTS, nosniff, and Referrer-Policy From the Blog Site Block #497

Closed

Description

@ptr727

Summary

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.

🤖 Generated with Claude Code

Activity

  1. ptr727 commented on Oct 24, 2026

    @ptr727
    OwnerAuthor

    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.
    • Cache lifetimes. The family block's @svg (a week) and @code classes 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, or deploy/README.md says why they differ.
    • 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.

  2. ptr727 commented on Oct 24, 2026

    @ptr727
    OwnerAuthor

    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
    5 Root images (favicon.ico, apple-touch-icon.png, og-image.png) public, max-age=3600 no-cache, through @page 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.
    • Cache-Control on pages, redirects, and the error page is no-cache on both. Both serve precompressed br and gzip, the error pages included since Defeat the Edge Cache in the Media Check and Name the Zone Settings #496.
    • 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.

  3. ptr727 commented on Oct 24, 2026

    @ptr727
    OwnerAuthor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions