How this site is built
A static Astro site on Cloudflare, with local search, opt-in comments and a strict Content-Security-Policy.
Architecture Open source astro cloudflare csp privacyContents
This publication is deliberately boring technology: Markdown files in a Git repository, turned into static HTML at build time, and served from a CDN. This article doubles as a reference for the formatting the site supports.
Publishing pipeline
Every change goes through a pull request, which GitHub Actions checks: types,
formatting, a full build and a validation of the generated pages. Once it is
merged into main, Cloudflare builds the site, indexes it for search and
publishes it.
No server renders pages on request, and there is no database. The attack surface of the public site is a set of files.
What runs in your browser
Almost nothing. Each page ships a few kilobytes of JavaScript for the menu, the theme switch and the copy buttons on code blocks. Search and comments load their code only when you use them.
| Feature | Loaded when | Talks to |
|---|---|---|
| Search | You open search | This site only |
| Comments | You press the button | comments.vciso.ch |
| Fonts | Page load | This site only |
Headers
Cloudflare adds the security headers from a _headers file in the build
output:
/*
Content-Security-Policy: default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self'
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
Checking them takes one command:
curl -sI https://vciso.ch/ | grep -iE '^(content-security|strict-transport)'
Writing an article
An article is a Markdown file under src/content/blog/. Its frontmatter is
validated at build time1, so a typo in a date fails the build rather
than publishing a broken page.
title: "Zero Trust beyond the buzzwords"
description: "A practical look at Zero Trust architecture."
pubDate: 2026-11-02
topics: [cybersecurity, architecture]
tags: [zero-trust, identity]
Keyboard shortcuts are written with <kbd>: press Ctrl K
to search.
Footnotes
-
The schema lives in
src/content.config.ts. ↩