CSP for Stitches: Locking Down CSS-in-JS

If you use Stitches, CSP gets awkward fast. The problem is simple: Stitches is a CSS-in-JS library, and CSP is usually hostile to anything that injects styles at runtime. That does not mean they cannot work together. It means you need to be deliberate about how styles are rendered, when they are injected, and which CSP directives you allow. I’ve seen teams ship a pretty solid script-src policy, then quietly leave style-src 'unsafe-inline' forever because CSS-in-JS made cleanup feel painful. That is usually where the security story falls apart. ...

October 20, 2026 · 7 min · headertest.com

CSP for shadcn/ui: Nonces, Hashes, and Real Tradeoffs

shadcn/ui gives you a weird CSP problem compared to most component libraries: it is not really a library in the classic sense. You copy components into your app, own the code, and then your CSP story becomes your problem. That is good for flexibility, but it also means there is no single “shadcn/ui CSP policy.” The right policy depends on how you render styles, whether you use theme scripts, whether you pull in analytics, and whether your app is static, SSR, or edge-rendered. ...

June 16, 2026 · 7 min · headertest.com

CSP for React, Vue, Angular, and Next.js: Working Examples

Single Page Applications have a unique relationship with CSP. The development workflow depends on things CSP doesn’t like — eval for HMR, inline styles for CSS-in-JS, dynamic script loading. But in production, SPAs actually benefit enormously from CSP because they’re JavaScript-heavy. Here are configurations that work for each major framework, based on production deployments. React (Vite) Vite is the recommended build tool for new React projects. Development // vite.config.js export default defineConfig({ plugins: [react()], server: { headers: { 'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https: blob:; font-src 'self'; connect-src 'self' ws:; frame-ancestors 'none'" } } }); ```text You need `unsafe-eval` for Vite's hot module replacement. You need `ws:` in connect-src for the WebSocket connection Vite uses to push HMR updates. You need `blob:` in img-src because some bundlers use blob URLs for certain assets. These are development-only concessions. In production, you can be much stricter. ### Production Deploy via Nginx or your reverse proxy: location / { add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’; style-src ‘self’ ‘unsafe-inline’; img-src ‘self’ data: https: blob:; font-src ‘self’; connect-src ‘self’ https://api.yoursite.com; frame-ancestors ’none’; base-uri ‘self’” always; try_files $uri $uri/ /index.html; } ...

March 29, 2026 · 4 min · headertest.com