The decision that mattered most
Before writing a single line of CSS, the most consequential decision for this blog's security was architectural: no server-side runtime, no database, no login. Every other control downstream of that decision became simpler, because entire categories of risk simply don't exist here.
This is not a workaround. It's the same principle CISOs preach in every risk review: the cheapest mitigation is the one where the vulnerable component never exists in the first place.
Mapping OWASP Top 10 to a static architecture
| Risk | Typical exposure | Here |
|---|---|---|
| A01 Broken Access Control | Privilege escalation, IDOR | No accounts exist. Nothing to escalate to. |
| A02 Cryptographic Failures | Weak TLS, plaintext secrets | Cloudflare enforces TLS 1.3; no secrets stored anywhere |
| A03 Injection | SQL, command, template injection | No database, no shell execution, no template engine at runtime |
| A04 Insecure Design | Flawed business logic | No business logic — content is read-only |
| A05 Security Misconfiguration | Missing headers, defaults | Headers enforced at CDN edge for every response |
| A06 Vulnerable Components | Outdated libraries | npm audit in CI; no client-side framework shipped |
| A07 Auth Failures | Session hijacking, weak MFA | No authentication surface |
| A08 Integrity Failures | Supply chain compromise | Pinned Action SHAs, --ignore-scripts, no CDN script tags |
| A09 Logging Failures | Undetected breaches | Cloudflare Analytics; nothing sensitive to leak |
| A10 SSRF | Server makes attacker-controlled requests | No server-side HTTP client exists |
Eight of ten risks are architecturally eliminated. The remaining two — misconfiguration and supply chain — get explicit, verified controls.
What "verified" means here
Every CI run checks, not just assumes, the security posture:
# From the CI pipeline — fails the build if any header is missing
REQUIRED=("Content-Security-Policy" "Strict-Transport-Security" "X-Frame-Options" ...)
for header in "${REQUIRED[@]}"; do grep -q "$header" public/_headers || exit 1
done
# XSS guard — scans every rendered post for injection vectors
# before Cloudflare ever sees the build output
if re.search(r'<script', post_body, re.I): fail("Script tag found in sanitised post body")
If a header gets accidentally deleted in a future edit, or a Markdown file somehow smuggles a <script> tag past DOMPurify, the build fails loudly — in CI, before deployment, not in production after an attacker finds it.
The publishing workflow, end to end
- Write the article in Markdown locally
git pushto themainbranch- Cloudflare Pages detects the push and pulls the repo directly
- Cloudflare runs
npm run build, which sanitises the Markdown, minifies and obfuscates the JavaScript via Terser, and minifies CSS via cssnano - The static output deploys to Cloudflare's edge network globally
No API tokens change hands. No manual deploy step. No SSH key on a server somewhere. The only privileged action in this entire pipeline is the git push itself, and that's protected by your GitHub account's own authentication.
Why obfuscation matters here
Minification alone shrinks file size. Obfuscation goes further — mangling variable names, removing all comments, and collapsing logic so that anyone viewing source sees compressed, unreadable output rather than a readable blueprint of how the theme toggle or reading-progress bar works.
// Before (source, readable)
function applyTheme(isDark) { document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
} // After (shipped to browser, Terser-obfuscated)
function n(o){document.documentElement.setAttribute("data-theme",o?"dark":"light")}
This isn't a security boundary on its own — client-side obfuscation never is, since anyone can still read the network response. But it raises the cost of casual inspection and keeps the production bundle small, which matters for both performance and reducing what an automated scraper can trivially template-match against known frameworks.
Conclusion
The strongest security control available to a personal blog isn't a clever header or a sophisticated WAF rule — it's removing the attack surface entirely. A static site with verified headers, sanitised content, and a supply-chain-hardened build pipeline closes more doors than most production web applications ever manage to lock.