CSP FOR STATIC SITES
The directives that actually matter for static sites in 2026, plus the FAL + Supabase + Pagefind playbook from gautamkhorana.com.
What CSP is for
Content Security Policy is an HTTP header that tells the browser which origins are allowed to load resources for your page. Done right, it prevents most XSS, clickjacking, and supply-chain attacks. Done wrong, it silently breaks features in ways that take days to diagnose.
Static sites tend to under-document their CSP because the perceived risk is lower. The reality is that a static site with a missing CSP directive can break video playback, image loading, search functionality, or font rendering, and the browser console message is the only signal you get.
Per-resource-type mapping
The directive that controls each resource type:
img-src for img tags. media-src for video, audio, source elements. frame-src for iframe. connect-src for fetch and WebSocket. script-src for script tags. style-src for style tags and link rel=stylesheet. font-src for @font-face.
Critical: default-src "self" does NOT cover video from a third-party host. A video from Supabase Storage will silently fail to play with only its poster visible if you forget media-src. Browser console shows the directive that blocked it; check that first when video mysteriously will not play.
The May 2026 incident
On gautamkhorana.com in May 2026, a Kling MP4 hosted on Supabase Storage rendered the HTML correctly but never played. The poster image showed; the video would not start. Twenty minutes of debugging later, the answer was that CSP had no media-src directive and the browser was silently blocking the video element.
Fix was a one-line addition to netlify.toml: media-src "self" https://*.supabase.co. The poster-but-no-playback symptom is the giveaway pattern: it means CSP allows the image but not the video.
Static-search libraries need wasm and worker permissions
Pagefind, Stork, Tinysearch, and other static-site search libraries use WebAssembly plus Web Workers. CSP must include script-src "wasm-unsafe-eval" AND worker-src "self" blob: or the search hangs forever on "Searching..." with no console error.
Second incident on gautamkhorana.com in May 2026: search drawer stuck on "Searching..." because pagefind.init() silently failed when wasm was blocked. Fix was adding "wasm-unsafe-eval" to script-src and "self" blob: to worker-src.
The minimum-viable CSP for FAL + Supabase + Pagefind sites
Required:
img-src self https://*.supabase.co https://*.fal.media, for hero images and FAL-generated assets. media-src self https://*.supabase.co, for video. connect-src self https://*.supabase.co, for Supabase API calls. script-src self "wasm-unsafe-eval", for Pagefind. worker-src self blob:, for Pagefind workers. style-src self "unsafe-inline", for Astro scoped styles. font-src self, for self-hosted fonts.
Whenever you add a new asset host (Cloudinary, Bunny, R2, anything), audit netlify.toml or vercel.json and add the host to every directive that resource type might use. The cost of getting this wrong is a silent break that surfaces in production.
Testing CSP changes
CSP-Report-Only mode lets you test policy changes in production without breaking anything. The browser logs would-be violations to a reporting endpoint instead of blocking the request. Run any new directive in report-only for a week, review the violations, then promote to enforcing.
Most CSP failures are silent in production. A reporting endpoint is the only way to know what you have actually broken. Set one up before shipping any non-trivial CSP change.
WHEN YOU ARE READY TO TALK
If you are mid-build on something this guide touches and want a second pair of eyes, the fastest path is a 30-minute call.
BOOK YOUR 30-MIN CALL