cloudflare-hygiene
Skill6 starsChanged 44 days agoMITView on GitHub
Audit + harden + optimize live Cloudflare zones/domains - read-only sweep of each zone (SSL/TLS mode, HSTS, min TLS, TLS 1.3, Always-Use-HTTPS, Brotli, HTTP/3, 0-RTT, Early Hints, caching, security level, Bot Fight Mode, WAF, DNS proxy/TTL + SPF/DKIM/DMARC hygiene, DNSSEC) -> ok/attention/action report grouped by category -> per-category confirm -> apply via Cloudflare MCP. Use when the user says "harden my cloudflare", "optimize my cloudflare zones", "audit my domains", "check my cloudflare settings", "secure my dns", "harden my dns", "hide my origin IP", "set up HSTS/DNSSEC", "am I leaking my origin", "is my cloudflare config optimal", or "tune cloudflare". For BUILDING on Cloudflare (Workers/Pages/KV/D1/Terraform) use the cloudflare skill instead.
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 16
All work goes through mcp__cloudflare__execute, which runs a JS async arrow function - not a plain REST caller.
- Very specific: line 68
No churn: a setting already at optimal is ok, not an action - don't PATCH it to look busy.
- Distinctive: line 69
Never report a finding this file predicts - the verdict comes from the run, not from here.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Cloudflare Hygiene
Periodic posture sweep over live zones: preflight -> sweep (read-only) -> report -> confirm -> apply -> verify. Optimization = real wins only: TLS strict, HTTPS forced, modern protocols on, DNS proxied + mail auth complete. No theater (no settings churned that already match optimal, no proxying records that must stay direct). Building on Cloudflare (Workers/Pages/KV/D1) -> cloudflare, hand off.
Preflight
- Enumerate zones -
GET /zones(paginate). List name · id · plan. Warn: full sweep across N zones takes a moment - Permission scope - this skill mutates zone settings + DNS, needs an authorized session that can write them. Probe once against the first zone:
GET /zones/{firstZoneId}/settings/ssl. 9109/10000 (Unauthorized) -> scope too narrow, stop and surface the upgrade path (see Access setup below) before sweeping. Don't limp through a settings-blind run All work goes through
mcp__cloudflare__execute, which runs a JS async arrow function - not a plain REST caller. Every call in this skill is shorthand for that shape:async () => cloudflare.request({ method: "GET", path: `/zones/${zoneId}/settings/ssl` })accountIdis pre-injected; zone ids come from step 1. Returns{success, status, result, errors, result_info}- readerrors[0].codefor the 9109 probe,result_info.total_pagesto paginate. Batch a whole category's GETs into oneexecutecall (loop inside the function, return an object) - one round-trip per category beats one per setting.mcp__cloudflare__search(OpenAPI spec, refs resolved) for endpoint/body shapes;mcp__cloudflare__docsfor product behavior
Sweep (Read-Only)
Per zone, every category below - each shows in the report, clean ones as "ok", never silently dropped. GETs only here; no mutation. Full endpoint + optimal-value matrix lives in references/best-practices.md; scripts/audit.mjs is the reference read-only sweep logic (GETs + verdict classifier).
- SSL/TLS -
settings/ssl(wantstrict);settings/always_use_https(on);settings/min_tls_version(≥1.2);settings/tls_1_3(on);settings/automatic_https_rewrites(on);settings/security_headerHSTS (enabled, max-age ≥6mo, includeSubDomains; preload = opt-in only) - Security / WAF / bot -
rulesetsphases (http_request_firewall_manageddeployed?ddos_l7present?);settings/security_level(medium);bot_management(Bot Fight Mode on - Free tier);settings/email_obfuscation(on);settings/browser_check(on); custom firewall ruleset (http_request_firewall_custom- any bespoke rules?); rate limiting ruleset (http_ratelimitphase - login/API endpoints protected?); IP access rules / Zone Lockdown (firewall/access_rules/rules- stale allowlists?); Page Shield (page_shield- client-side/Magecart, plan-gated); Authenticated Origin Pulls (settings/tls_client_auth- mTLS edge->origin so origin only trusts CF); Leaked Credential Check (leaked-credential-checks- alerts on breached creds, plan-gated). Under Attack mode = situational (incident only), never default-on - Performance / caching -
settings/brotli(on);settings/early_hints(on);settings/cache_level(aggressive/standard);settings/browser_cache_ttl;settings/rocket_loader(case-by-case - can break JS, report don't force) - Network protocols -
settings/http3(on);settings/0rtt(on, idempotent-GET caveat);settings/ipv6(on);settings/websockets(on). HTTP/2 is automatic with the proxy - no readable setting, don't hunt for one - DNS hygiene -
dns_records: proxiable A/AAAA/CNAME should be proxied (orange) unless intentionally direct (flag grey-cloud = origin exposed);dnssec(active); parse TXT for SPF (rootv=spf1), DKIM (*._domainkey), DMARC (_dmarc.<zone>- flag missing as action); dangling CNAMEs -> takeover risk; low TTLs on stable records - Analytics / observability - Web Analytics (
/accounts/{acct}/rum/site_info/list- privacy-first, free; enabled for this zone?); Security Events summary (GraphQLfirewallEventsAdaptive- last 24h threats blocked, top rules/countries -> posture context, not a setting); Logpush (logpush/jobs- paid/Enterprise, report gap only). Pull the security-events summary read-only so the report shows what the WAF is actually catching, not just whether it's on
Report
Table per zone × category: check · current · optimal · verdict (ok / attention = watch / action = fix available, named). Lead per zone: "Zone X: N ok, N attention, N actions". Then AskUserQuestion multiSelect over the actions only. Anything that can break traffic gets its own option, side effects stated first.
Actions (Each Confirmed, Side Effects Stated First)
- SSL mode -> Full (strict):
PATCH settings/ssl {value:"strict"}- breaks if origin lacks a valid cert. Confirm origin has a cert (Cloudflare Origin CA or real CA) before flipping - Always Use HTTPS / Auto HTTPS Rewrites / min TLS 1.2 / TLS 1.3 on:
PATCHeach - low risk; min-TLS bump can lock out ancient clients, name it - HSTS:
PATCH settings/security_header {value:{strict_transport_security:{enabled,max_age,include_subdomains}}}(single-setting body wraps invalue;nosniffalso available). Hard to undo - browsers cache it; preload is near-permanent. Default no preload unless user explicitly opts in - Brotli / Early Hints / HTTP/3 / 0-RTT / IPv6 / WebSockets on:
PATCH settings/{id} {value:"on"}each - safe, idempotent - Email obfuscation / browser check on:
PATCH settings/{id} {value:"on"}- safe - Bot Fight Mode on:
PUT bot_management {fight_mode:true}(whole-object PUT, not a setting PATCH) - safe; can challenge some automation - Rate limiting rule:
PUT rulesets/phases/http_ratelimit/entrypoint- protect login/API/expensive endpoints. Set thresholds from real traffic (use the analytics summary) - too tight = legit 429s. Confirm the matched path + threshold - Authenticated Origin Pulls:
PATCH settings/tls_client_auth {value:"on"}- origin must be configured to require the CF client cert first, else all traffic 502s. Two-step, origin-side change owned by user - Page Shield:
PUT page_shield {enabled:true}- plan-gated (Pro+); if Free errors, report the gap, don't churn - Leaked Credential Check:
POST leaked-credential-checks {enabled:true}- plan-gated; same fallback - Prune stale IP access rules:
DELETE firewall/access_rules/rules/{id}- confirm each; an old allow-rule can be a bypass hole - Enable Web Analytics:
POST /accounts/{acct}/rum/site_infofor the zone - free, privacy-first, no cookie banner; gives RUM without GA. Safe - Enable DNSSEC:
PATCH dnssec {status:"active"}-> returns DS record; incomplete until user adds the DS record at the registrar - give them the DS values, it's a two-step - Proxy a grey-cloud record:
PATCH dns_records/{id} {proxied:true}- never proxy mail (MX/its A), verification, or services needing direct origin. Confirm per record - Add DMARC: propose
_dmarc.<zone>TXT (startv=DMARC1; p=none; rua=mailto:...for monitoring), confirm value,POST dns_records
Access Setup (When Preflight Fails)
The cloudflare MCP is a remote OAuth server (https://mcp.cloudflare.com/mcp) - there is no API token in its config to widen. A 9109/10000 means the authorized Cloudflare account lacks the permission, not that a token needs editing. Two ways out:
- Fix the account - the OAuth'd user needs Zone.Settings:Edit + DNS:Edit + Zone:Read on the target zones (plus Bot Management / SSL where the plan allows). Change the member's role in the Cloudflare dashboard -> Manage Account -> Members, then reconnect the MCP (
/mcp) so the session picks up the new scope. Re-run preflight after - Fall back to the script -
scripts/audit.mjstakesCLOUDFLARE_API_TOKENdirectly, so a scoped token (My Profile -> API Tokens) gets a read-only sweep without touching the MCP session. Read-only: applies still need the MCP path above
Rules
- Sweep = read-only. Change state only via confirmed actions, one at a time -> verify (re-GET, confirm new value) -> next
- Side effects before confirm, always: SSL-mode origin break, min-TLS client lockout, HSTS/preload permanence, DNSSEC two-step, proxying a direct-only record
- No churn: a setting already at optimal is
ok, not an action - don't PATCH it to look busy. Clean zone -> "posture optimal", stop - Never report a finding this file predicts - the verdict comes from the run, not from here. Baked-in findings are how an audit skill rots
- Report-only is valid - fixing is optional, never assumed
- Free-plan limits: some managed-WAF rules, Bot Management depth, and Page Rules count are plan-gated - report the gap, don't pretend it's fixable on Free
- Boundaries: Workers/Pages/KV/D1/R2/Terraform ->
cloudflare; registrar-side DNS, origin-server config, email DNS content policy -> out of scope, name it
In this repository21 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- blog-writeSkill
- git-branch-cleanupSkill
- git-branch-createSkill
- git-branch-syncSkill
- git-commitSkill
- git-issue-createSkill
- git-issue-startSkill
- git-issue-triageSkill
- git-pr-createSkill
- herdr-dispatchSkill
- humanizeSkill
- macos-cleanupSkill
- macos-maintenanceSkill
- obsidian-markdownSkill
- spec-applySkill
- spec-archiveSkill
- spec-exploreSkill
- spec-proposeSkill
- spec-verifySkill
- vercel-hygieneSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

