← All articlesSecurity

Why I Built CyberDen as a Static Site — And What That Means for Security

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

RiskTypical exposureHere
A01 Broken Access ControlPrivilege escalation, IDORNo accounts exist. Nothing to escalate to.
A02 Cryptographic FailuresWeak TLS, plaintext secretsCloudflare enforces TLS 1.3; no secrets stored anywhere
A03 InjectionSQL, command, template injectionNo database, no shell execution, no template engine at runtime
A04 Insecure DesignFlawed business logicNo business logic — content is read-only
A05 Security MisconfigurationMissing headers, defaultsHeaders enforced at CDN edge for every response
A06 Vulnerable ComponentsOutdated librariesnpm audit in CI; no client-side framework shipped
A07 Auth FailuresSession hijacking, weak MFANo authentication surface
A08 Integrity FailuresSupply chain compromisePinned Action SHAs, --ignore-scripts, no CDN script tags
A09 Logging FailuresUndetected breachesCloudflare Analytics; nothing sensitive to leak
A10 SSRFServer makes attacker-controlled requestsNo 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

  1. Write the article in Markdown locally
  2. git push to the main branch
  3. Cloudflare Pages detects the push and pulls the repo directly
  4. Cloudflare runs npm run build, which sanitises the Markdown, minifies and obfuscates the JavaScript via Terser, and minifies CSS via cssnano
  5. 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.