skills/security/common-security/secure-headers/SKILL.md
HTTP security headers configuration guide. Use this skill when hardening web applications, configuring CSP, or setting up security headers. Activate when: security headers, CSP, Content-Security-Policy, HSTS, X-Frame-Options, CORS headers, clickjacking prevention, helmet.
npx skillsauth add latestaiagents/agent-skills secure-headersInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Configure HTTP security headers to protect against common web attacks.
| Header | Purpose | Priority | |--------|---------|----------| | Content-Security-Policy | XSS prevention | HIGH | | Strict-Transport-Security | Force HTTPS | HIGH | | X-Frame-Options | Clickjacking | HIGH | | X-Content-Type-Options | MIME sniffing | MEDIUM | | Referrer-Policy | Privacy | MEDIUM | | Permissions-Policy | Feature control | MEDIUM |
const helmet = require('helmet');
app.use(helmet({
// Content Security Policy
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.example.com"],
styleSrc: ["'self'", "'unsafe-inline'"], // Consider removing unsafe-inline
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.example.com"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
frameAncestors: ["'none'"],
formAction: ["'self'"],
baseUri: ["'self'"],
upgradeInsecureRequests: []
}
},
// HTTP Strict Transport Security
hsts: {
maxAge: 31536000, // 1 year
includeSubDomains: true,
preload: true
},
// Prevent clickjacking
frameguard: { action: 'deny' },
// Prevent MIME type sniffing
noSniff: true,
// Referrer Policy
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
// Permissions Policy
permittedCrossDomainPolicies: { permittedPolicies: 'none' }
}));
// Additional headers
app.use((req, res, next) => {
// Permissions Policy (formerly Feature-Policy)
res.setHeader('Permissions-Policy',
'geolocation=(), microphone=(), camera=(), payment=()');
// Cross-Origin policies
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
next();
});
server {
listen 443 ssl http2;
server_name example.com;
# SSL configuration
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# Security Headers
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; object-src 'none'; frame-ancestors 'none';" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
# Hide server version
server_tokens off;
}
<VirtualHost *:443>
ServerName example.com
# Security Headers
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
# Hide server version
ServerTokens Prod
ServerSignature Off
</VirtualHost>
// Start restrictive, then loosen as needed
const cspDirectives = {
// Default fallback for all resource types
defaultSrc: ["'self'"],
// JavaScript sources
scriptSrc: [
"'self'",
// "'unsafe-inline'", // Avoid if possible
// "'unsafe-eval'", // Never use
"'nonce-${nonce}'", // Better than unsafe-inline
"https://trusted-cdn.com"
],
// CSS sources
styleSrc: [
"'self'",
"'unsafe-inline'", // Often needed for frameworks
"https://fonts.googleapis.com"
],
// Image sources
imgSrc: [
"'self'",
"data:", // For inline images
"https:" // Any HTTPS image
],
// Font sources
fontSrc: [
"'self'",
"https://fonts.gstatic.com"
],
// AJAX/WebSocket/Fetch destinations
connectSrc: [
"'self'",
"https://api.example.com",
"wss://ws.example.com"
],
// <object>, <embed>, <applet>
objectSrc: ["'none'"],
// <frame>, <iframe>
frameSrc: ["'none'"],
// Who can frame this page
frameAncestors: ["'none'"],
// Form submission targets
formAction: ["'self'"],
// <base> tag restrictions
baseUri: ["'self'"],
// Upgrade HTTP to HTTPS
upgradeInsecureRequests: []
};
// Generate nonce per request
app.use((req, res, next) => {
res.locals.nonce = crypto.randomBytes(16).toString('base64');
next();
});
// Set CSP with nonce
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy',
`default-src 'self'; script-src 'self' 'nonce-${res.locals.nonce}'`);
next();
});
<!-- In template -->
<script nonce="<%= nonce %>">
// Inline script that will execute
</script>
// Report-only mode (doesn't block, just reports)
const cspReportOnly = {
...cspDirectives,
reportUri: '/csp-report'
};
app.use(helmet.contentSecurityPolicy({
directives: cspReportOnly,
reportOnly: true // Test before enforcing
}));
// CSP report endpoint
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
console.log('CSP Violation:', req.body);
res.status(204).end();
});
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-age: Time in seconds browser should remember HTTPS-only
includeSubDomains: Apply to all subdomains
preload: Allow inclusion in browser preload list
X-Frame-Options: DENY # Never allow framing
X-Frame-Options: SAMEORIGIN # Only same origin can frame
Referrer-Policy: no-referrer # Never send referrer
Referrer-Policy: same-origin # Only same origin
Referrer-Policy: strict-origin-when-cross-origin # Recommended
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=(), usb=()
# Allow specific origins
Permissions-Policy: geolocation=(self "https://maps.example.com")
# Check headers with curl
curl -I https://example.com
# Online tools
# https://securityheaders.com
# https://observatory.mozilla.org
# Check CSP specifically
# https://csp-evaluator.withgoogle.com
// Issue: "Refused to execute inline script"
// Fix: Use nonce or move to external file
scriptSrc: ["'self'", `'nonce-${nonce}'`]
// Issue: "Refused to load stylesheet"
// Fix: Add the source
styleSrc: ["'self'", "https://cdn.example.com"]
// Issue: "Blocked frame with origin"
// Fix: Adjust frame-ancestors or X-Frame-Options
frameAncestors: ["'self'", "https://trusted-site.com"]
// Issue: Breaking third-party widgets
// Fix: Add specific sources (not *)
scriptSrc: ["'self'", "https://widget.example.com"]
development
Test skills for correct activation, content quality, and regression — both automated checks (frontmatter validity, lint) and manual verification (query-suite activation testing). Covers CI integration and how to catch skill regressions before users do. Use this skill when adding skills to a repo, setting up CI for a skill library, or debugging "the skill exists but doesn't work". Activate when: test skills, validate skills, skill CI, skill linting, skill activation test, skill regression.
documentation
Write the YAML frontmatter for a SKILL.md file so it activates reliably — name, description, and activation keywords that the model matches against. Covers length, tone, and the most common frontmatter mistakes. Use this skill when authoring a new skill, fixing a skill that isn't auto-activating, or reviewing skills for publication. Activate when: SKILL.md frontmatter, skill description, skill activation, skill YAML, write a skill, author a skill.
development
Design skills that fire at the right moment — neither over-eager (noise) nor under-eager (silent). Covers activation specificity, trigger phrases, disambiguation between overlapping skills, and debugging activation. Use this skill when multiple skills could fire on the same query, a skill never fires, or a skill fires too often. Activate when: skill won't activate, skill over-activates, overlapping skills, skill triggers, skill selection, skill disambiguation.
development
Structure SKILL.md content so the model reads just enough — concise summary up front, progressively deeper detail, examples on demand. Covers section ordering, length budgets, when to split into multiple skills. Use this skill when writing or refactoring a skill body, one skill has grown too long, or a skill is wordy but not useful. Activate when: SKILL.md structure, skill content, skill too long, split skill, progressive disclosure, skill body.