# Security Headers

> Use when reviewing or hardening a web app's HTTP response headers — CSP, HSTS, and the rest — knowing which actually reduce risk and which are theatre.

- Skill: `jihedbfr-art/security-headers` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add jihedbfr-art/security-headers`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jihedbfr-art/security-headers/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: jihedbfr-art (https://skillmd.com/u/jihedbfr-art)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/jihedbfr-art/security-headers

---




## Prerequisites
- Target system, dependencies and environment configured.

## Usage
### Purpose

Security headers are defence-in-depth: they don't fix vulnerabilities, they shrink the blast radius when one exists. A good CSP turns many XSS bugs into non-events; HSTS closes the downgrade window. This skill covers which headers earn their place, how to check them, and how to set them without breaking the app.

### When to use it

During a web assessment (quick win to check) and during hardening. Fair warning: header scanners hand out low-severity findings generously — this skill is about knowing which matter so you don't drown a report in noise.

### Procedure

1. Pull the response headers and read what's there — and what's missing:
   ```
   curl -sI https://app.tld | grep -iE 'content-security|strict-transport|x-frame|x-content|referrer|permissions'
   ```
2. Assess the four that carry real weight first:
   - **Content-Security-Policy** — the big one. Restricts where scripts/resources load from; a strict policy blunts XSS.
   - **Strict-Transport-Security** — forces HTTPS, closing SSL-strip. Needs a long `max-age` and, ideally, `includeSubDomains; preload`.
   - **X-Frame-Options / CSP frame-ancestors** — stops clickjacking by controlling who can frame the page.
   - **X-Content-Type-Options: nosniff** — stops MIME sniffing that turns an upload into script.
3. For CSP specifically, judge the *content*, not just its presence. A policy with `unsafe-inline` and `unsafe-eval` on `script-src`, or a `default-src *`, is close to no policy at all.
4. Note the ones that are now largely obsolete or misunderstood so you don't over-report them: `X-XSS-Protection` is deprecated (turn it off, `0`), and `Server`/`X-Powered-By` version banners are info leaks worth removing but not vulnerabilities.
5. Verify HSTS won't lock users out before recommending `preload` — once preloaded, HTTPS is mandatory for the whole domain and reversal is slow.

### Cheatsheet

```bash
curl -sI https://app.tld

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
X-Frame-Options: DENY        # legacy; frame-ancestors in CSP supersedes it
```

### Reading the output

- **No CSP, or a CSP with `unsafe-inline`/`unsafe-eval`/wildcards** — the meaningful finding. It means an XSS bug gets full impact.
- **Missing or short HSTS** — users are exposed to downgrade on first/again visits. `max-age` under a few months is weak.
- **Framing allowed** (no `X-Frame-Options` and no `frame-ancestors`) — clickjacking is on the table for sensitive actions.
- **Present-but-weak beats absent-and-scary in reports:** rank by real risk. A missing `Permissions-Policy` is minor; a wildcard CSP is not.

### The fix

Set the four load-bearing headers at the edge (reverse proxy or framework middleware) so every response carries them:

- Build the **CSP** iteratively — start in `Content-Security-Policy-Report-Only` mode, collect violation reports, tighten until you can drop `unsafe-inline` (move inline scripts to files or use nonces/hashes), then enforce.
- **HSTS** with a two-year `max-age` and `includeSubDomains`; add `preload` only once you're certain every subdomain does HTTPS.
- `nosniff` and `frame-ancestors 'none'` (or a tight allowlist) — cheap, no downside for most apps.
- Strip `Server`/`X-Powered-By` version details.

### Pitfalls

- **CSP set-and-forget.** A copied policy with `unsafe-inline` gives false comfort. If it doesn't remove inline script, it doesn't stop much XSS.
- **HSTS preload regret.** Preloading before subdomains support HTTPS locks people out. Confirm coverage first.
- **Over-reporting.** A wall of "missing header" lows buries the one that matters. Lead with CSP quality and HSTS.
- **Headers as a substitute for fixes.** They reduce impact; they don't remove the underlying bug. Fix the injection *and* set the header.

### References

- OWASP Secure Headers Project
- OWASP Content Security Policy Cheat Sheet
- MDN — HTTP headers (CSP, Strict-Transport-Security)

## Inputs
- Relevant source code, logs, network traces, or system specifications.

## Outputs
- Analysis findings, security audit report, or generated code artifacts.
