Set up security headers
Skill VelimirMueller/claude_development_skills/skills/frontend/set-up-security-headers
Use when hardening a frontend SPA's delivery — defines a Content-Security-Policy and standard security headers delivered at the edge via Netlify, dependency hygiene via Dependabot, and an honest note on the XSS surface.From its SKILL.md
npx -y skills add VelimirMueller/claude_development_skills --skill set-up-security-headersAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
3.8 KB, as published. Nobody here has run it
Set Up Security Headers
Security headers are delivered by the host, not the bundle. This skill writes a strict, SPA-appropriate header set for Netlify and wires the CSP's connect-src to the origins the app actually calls.
1. Audit current state
ls netlify.toml public/_headers .github/dependabot.yml 2>/dev/null
grep -in "content-security-policy\|strict-transport" netlify.toml public/_headers 2>/dev/null
grep -n "VITE_API_URL\|VITE_REALTIME_URL" src/libs/env.ts 2>/dev/null # origins for connect-src
Prerequisites: a Netlify deploy (see configure-ci). Recommended: validate-env (the CSP reads VITE_API_URL / VITE_REALTIME_URL).
2. Decide
- No headers → full setup. Partial → add the missing headers/directives. Present → confirm
connect-srccovers current API + realtime origins.
3. The header block (Netlify)
Add to netlify.toml (create if absent — this is the [[headers]] block; configure-ci owns the [build] block, so merge, don't overwrite):
[[headers]]
for = "/*"
[headers.values]
Content-Security-Policy = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self' https://api.example.com wss://realtime.example.com; frame-ancestors 'none'; base-uri 'self'; object-src 'none'"
Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"
Permissions-Policy = "camera=(), microphone=(), geolocation=()"
Replace https://api.example.com / wss://realtime.example.com with the origins of VITE_API_URL / VITE_REALTIME_URL (scheme + host, no path). Drop the wss: origin if realtime isn't set up.
4. CSP notes for a Vite SPA
- Vite emits external scripts in prod, so
script-src 'self'needs no'unsafe-inline'. (These headers apply to the deployed build; the dev server is looser.) style-srckeeps'unsafe-inline'because Tailwind/runtime styles inject inline<style>. To drop it, adopt a nonce/hash — seesecurity-patterns.md.connect-srcbreaks silently: omit your API orwss://origin and those requests fail with a console CSP error. That's why it reads from the env seam.
5. Dependency hygiene
.github/dependabot.yml:
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly
groups:
minor-and-patch:
update-types: [minor, patch]
Pair with a pnpm audit step in CI (configure-ci). Automation proposes; a human approves each bump.
6. The XSS surface (the honest note)
React and Vue escape interpolated values by default. The real hole is dangerouslySetInnerHTML (React) / v-html (Vue) — they bypass escaping. Avoid them; if unavoidable, sanitize with DOMPurify first. A strict CSP is defense-in-depth, not a substitute.
7. Verify
# after a deploy preview is live:
curl -sI https://<deploy-preview-url>/ | grep -i "content-security-policy\|strict-transport"
Load the app: the console shows no CSP violations (every API / realtime / font / image origin is allowed). Grade it at securityheaders.com.
References
- ./security-patterns.md — edge-vs-meta delivery, the
connect-src-from-env rule, the inline-style nonce path, dependency hygiene, the XSS surface, and other hosts. - ../validate-env/SKILL.md — the
envseam supplying theconnect-srcorigins. - ../configure-ci/SKILL.md — owns the
netlify.toml[build]block and thepnpm auditstep.