Cloudflare Zone Hardening

Step-by-step zone hardening for Cloudflare-fronted sites: TLS minimums, cipher suites, edge HTTPS redirect, CAA records, and why the HSTS toggle stays off when security headers live in Worker middleware.

Prerequisites

Before starting, confirm the following:

  • You have dashboard access to the Cloudflare zone for the target domain
  • The site is served by a Cloudflare Worker with the security-headers middleware deployed (the src/middleware.ts / src/lib/security-headers.ts pattern, see the launch checklist)
  • curl and dig are available on your local machine
  • You know which DNS records already exist on the zone, especially any existing CAA records

Warning: This procedure assumes the operating principle I apply to every zone: code is the source of truth for security headers; the dashboard is for TLS settings and redirects only. If your security headers come from somewhere else (a Transform Rule, a static config), fix that first, otherwise the verification step at the end will mislead you.

Step 1: Confirm the Worker Is Serving Security Headers

Do not harden a zone whose headers are coming from the wrong place. Check what the edge actually returns:

DOMAIN=stonefyr.com
curl -sI "https://$DOMAIN" | grep -iE "strict-transport|content-security|x-frame|referrer-policy"

You should see Strict-Transport-Security, a Content-Security-Policy containing a nonce- value, X-Frame-Options, and Referrer-Policy.

Verify: Run the curl twice. The nonce- value in the CSP must change between requests. A static nonce, or 'unsafe-inline' in script-src, means the Worker middleware is not attaching its headers and something else (usually a Transform Rule) is masking it. Stop and fix the middleware attach path before continuing.

Step 2: Set the TLS Edge Settings

In the dashboard, under SSL/TLS → Edge Certificates, set three values:

Setting Value Why
Minimum TLS Version TLS 1.2 Uncaps the SSL Labs grade from B
TLS 1.3 Enabled Modern handshake
Cipher Suites Modern Drops the CBC/3DES WEAK flags from scan results

Verify: Re-scan the domain with SSL Labs after the change propagates. The grade cap from the old TLS 1.0/1.1 floor should be gone and no cipher should be flagged WEAK.

Step 3: Enable Always Use HTTPS

Still under SSL/TLS → Edge Certificates, turn Always Use HTTPS to On.

The edge does the HTTP-to-HTTPS redirect before the request ever reaches the Worker, so the Worker does not burn CPU on it. The middleware keeps its own redirect as a fallback, but the edge should answer first.

Verify:

DOMAIN=stonefyr.com
curl -sI "http://$DOMAIN" | grep -iE "HTTP/|location"

Expected: an HTTP 301 whose only interesting header is Location: https://....

Step 4: Confirm the Dashboard HSTS Toggle Is OFF

Under SSL/TLS → Edge Certificates → HTTP Strict Transport Security, the toggle must be off.

This is deliberate and it is the step people second-guess. The HSTS header lives in code, in the Worker middleware, where it is versioned and reviewed. Enabling the dashboard toggle creates a second source of truth, and the two drift. It also caused hstspreload.org "HSTS over HTTP" warnings on stonefyr.com and mattsdeliciousmeat.com, because the dashboard happily attaches HSTS to plain-HTTP responses where browsers must ignore it.

Verify:

DOMAIN=stonefyr.com
curl -sI "http://$DOMAIN" | grep -i strict
curl -sI "https://$DOMAIN" | grep -i strict

Expected: no Strict-Transport-Security on the HTTP 301, exactly one Strict-Transport-Security on the HTTPS response.

Step 5: Add CAA Records

Under DNS → Records, add CAA records locking certificate issuance to the two CAs Cloudflare actually uses for these zones:

Type Name Tag Value
CAA @ issue pki.goog
CAA @ issuewild pki.goog
CAA @ issue letsencrypt.org
CAA @ issuewild letsencrypt.org

Both issue and issuewild for both issuers, four records total.

Verify:

DOMAIN=stonefyr.com
dig +short CAA "$DOMAIN" | sort

Expected output lists issue and issuewild entries for letsencrypt.org and pki.goog, nothing else.

Step 6: Remove Any Security-Header Transform Rules

Check Rules → Transform Rules on the zone. If any rule adds or modifies security headers, delete it.

A Transform Rule that injects a CSP is the most common way a broken Worker middleware goes unnoticed: the rule papers over the missing header, usually with a weaker policy containing 'unsafe-inline'. Security headers live in code, never in Transform Rules.

One holding-pattern exception exists in my zones: Matt's BBQ carries a Strip HSTS from HTTP responses Transform Rule until that site gets the Worker middleware port. Do not copy that pattern to new sites.

Verify: With the rule deleted, re-run the Step 1 curl. The CSP nonce must still be present and still rotate. If the CSP disappeared when the rule did, the Worker was never attaching headers, go fix the middleware (onRequest hook with setResponseHeader, not onBeforeResponse).

Step 7: Run the Full Verification Script

Paste-safe, no inline comments, so zsh does not choke on ? or parens:

DOMAIN=stonefyr.com
curl -sI "http://$DOMAIN" | grep -iE "HTTP/|location|strict|content-security"
curl -sI "https://$DOMAIN" | grep -iE "strict-transport|content-security|x-frame|referrer-policy"
dig +short CAA "$DOMAIN" | sort

Expected:

  • The HTTP request returns a 301 with only a Location header among the matches, no HSTS, no CSP
  • The HTTPS request returns the full security-header set: Strict-Transport-Security, Content-Security-Policy with a rotating nonce, X-Frame-Options, Referrer-Policy
  • The CAA query lists pki.goog and letsencrypt.org, both issue and issuewild

Run the HTTPS curl a second time and confirm the CSP nonce changed.

Rollback

Every dashboard setting in this runbook is independently reversible:

  1. TLS settings - set Minimum TLS Version, TLS 1.3, and Cipher Suites back to their previous values under SSL/TLS → Edge Certificates. Note the old values before changing them in Step 2.
  2. Always Use HTTPS - toggle off if it causes a redirect loop with some upstream config. Then find and fix the loop, the toggle itself is correct.
  3. CAA records - delete the four records if a certificate renewal fails. Before re-adding, confirm which CA the failing certificate actually comes from and include it.
  4. Deleted Transform Rule - if removing a CSP Transform Rule left pages without a CSP, do not recreate the rule. That state means the Worker middleware is not attaching headers. Fix the attach path in code (onRequest + setResponseHeader against the H3 native event) and redeploy.

After any rollback action, re-run the Step 7 verification script. Never leave the zone in an unverified state.

Maintenance Notes

  • Re-run the verification script after every dashboard change on the zone, no matter how small. Touching the dashboard without re-verifying is on the anti-pattern list for a reason.
  • Refuse these two requests whenever they come up:
    • Enabling the dashboard HSTS toggle on any site that has the Worker middleware (two sources of truth, guaranteed drift)
    • Adding security headers via Transform Rules
  • The full fleet was audited 2026-06-04 (headers and body checked on the same request): stonefyr.com, airassaultfireworks.com, arnoldsappliancerepair.com, valkyrienexus.com, valkyrienexus.dev all serve Worker-sourced nonce CSPs that rotate per request, with no 'unsafe-inline' in script-src. An earlier static-CSP issue on valkyrienexus.com was resolved as part of that audit.
  • Keep the Step 7 script handy as the per-zone smoke test. It covers the HTTP redirect, the HTTPS header set, and CAA in three commands, and it is deliberately free of inline comments so it pastes cleanly into zsh.
  • When a new site launches, this runbook runs immediately after the first deploy, followed by the email authentication rollout.
  • If a future zone needs a CA outside pki.goog and letsencrypt.org, extend the CAA set for that zone only. Do not loosen the records fleet-wide for one domain's requirement.
  • Record the audit date and per-domain results whenever the fleet is re-checked, the 2026-06-04 entry above is the template.
  • TXT and CAA records added here must stay DNS only. Proxying does not apply to them, and the email-auth runbook depends on the same rule.