HEADERS - nself-org/cli GitHub Wiki
Part of nself Sprint 17: Advanced Security
nself provides comprehensive security headers management to protect your applications from common web vulnerabilities including XSS, clickjacking, MIME-sniffing attacks, and more.
- Quick Start
- Security Headers Explained
- Content Security Policy (CSP)
- Configuration
- CLI Commands
- Testing & Validation
- Best Practices
- Troubleshooting
nself security headers shownself security headers configurenself security headers validate https://yourdomain.comnself security headers export nginx/includes/security-headers.confPurpose: Prevents Cross-Site Scripting (XSS) attacks by controlling which resources can be loaded.
Default: Moderate mode (balanced security and compatibility)
Content-Security-Policy: default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' data:;
connect-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests
OWASP Rating: A (Critical)
Purpose: Forces browsers to use HTTPS for all future requests.
Default: max-age=31536000; includeSubDomains (1 year)
Strict-Transport-Security: max-age=31536000; includeSubDomains
OWASP Rating: A (High)
Requirements:
- Only works with HTTPS
- Recommended max-age: 1 year (31536000 seconds)
- Include subdomains for full protection
Purpose: Prevents clickjacking attacks by controlling if site can be framed.
Default: DENY (most secure)
Options:
-
DENY- Never allow framing (recommended) -
SAMEORIGIN- Allow framing from same origin only
X-Frame-Options: DENY
OWASP Rating: A (High)
Purpose: Prevents MIME-sniffing attacks.
Value: nosniff (only valid value)
X-Content-Type-Options: nosniff
OWASP Rating: B (Medium)
Purpose: Legacy XSS filter for older browsers (modern browsers use CSP).
Default: 1; mode=block
X-XSS-Protection: 1; mode=block
OWASP Rating: C (Low - legacy header)
Note: Modern browsers rely on CSP instead. Kept for older browser compatibility.
Purpose: Controls how much referrer information is included with requests.
Default: strict-origin-when-cross-origin
Options:
-
no-referrer- Never send referrer -
no-referrer-when-downgrade- Send only on HTTPS→HTTPS -
same-origin- Send only to same origin -
strict-origin- Send origin only (not full URL) -
strict-origin-when-cross-origin- Full URL same-origin, origin only cross-origin (recommended)
Referrer-Policy: strict-origin-when-cross-origin
OWASP Rating: B (Medium)
Purpose: Controls which browser features can be used (formerly Feature-Policy).
Default: Deny camera, microphone, geolocation, payment, USB, interest-cohort (FLoC)
Permissions-Policy: camera=(), microphone=(), geolocation=(),
payment=(), usb=(), interest-cohort=()
OWASP Rating: B (Medium)
Common Permissions:
-
camera- Camera access -
microphone- Microphone access -
geolocation- Location access -
payment- Payment API -
usb- USB device access -
interest-cohort- FLoC (Google's tracking, disable recommended)
Purpose: Prevents Adobe Flash and PDF from loading content from this domain.
Value: none (recommended)
X-Permitted-Cross-Domain-Policies: none
OWASP Rating: C (Low)
nself provides three CSP modes:
Use Case: Production applications, static sites Trade-off: May break some features requiring inline scripts
export CSP_MODE=strictConfiguration:
- No
unsafe-inlineorunsafe-eval - Scripts/styles only from
'self' - No external CDNs (unless whitelisted)
Use Case: Most applications (default) Trade-off: Balanced security and compatibility
export CSP_MODE=moderateConfiguration:
- Allows
unsafe-inlineandunsafe-evalfor scripts/styles - Images from
'self',data:, andhttps: - Fonts from
'self'anddata: - Connects to
'self'only (unless whitelisted)
Use Case: Development, legacy applications Trade-off: Reduced security for broader compatibility
export CSP_MODE=permissiveConfiguration:
- Allows
unsafe-inline,unsafe-eval,data:, andhttps: - More lenient with external resources
- Still blocks
objecttags and enforcesbase-uri
For fine-grained control, use custom mode:
export CSP_MODE=custom
export CSP_DEFAULT_SRC="'self'"
export CSP_SCRIPT_SRC="'self' https://trusted-cdn.com"
export CSP_STYLE_SRC="'self' 'unsafe-inline' https://fonts.googleapis.com"
export CSP_IMG_SRC="'self' data: https:"
export CSP_FONT_SRC="'self' data: https://fonts.gstatic.com"
export CSP_CONNECT_SRC="'self' https://api.example.com"
export CSP_OBJECT_SRC="'none'"
export CSP_FRAME_SRC="'none'"
export CSP_BASE_URI="'self'"
export CSP_FORM_ACTION="'self'"
export CSP_FRAME_ANCESTORS="'none'"
export CSP_UPGRADE_INSECURE_REQUESTS="true"# Add CDN domain
nself security headers csp add-domain cdn.example.com
# Add API domain
nself security headers csp add-domain api.example.com
# List whitelisted domains
nself security headers csp list-domains
# Remove domain
nself security headers csp remove-domain cdn.example.comnself automatically generates optimized CSP for known services:
Hasura GraphQL:
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
connect-src 'self' ws: wss:;
Grafana:
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
connect-src 'self' https:;
img-src 'self' data: https:;
MinIO Console:
default-src 'self';
script-src 'self' 'unsafe-inline';
connect-src 'self' ws: wss:;
img-src 'self' data: blob:;
Add to .env file:
# Security Headers Mode
SECURITY_HEADERS_MODE=strict # strict, moderate, permissive
# Content Security Policy
CSP_MODE=moderate # strict, moderate, permissive, custom
CSP_CUSTOM_DOMAINS="cdn.example.com api.example.com"
# HSTS Configuration
HSTS_MAX_AGE=31536000 # 1 year in seconds
HSTS_INCLUDE_SUBDOMAINS=true
HSTS_PRELOAD=false # Only enable if submitting to HSTS preload list
# X-Frame-Options
X_FRAME_OPTIONS=DENY # DENY or SAMEORIGIN
# Referrer-Policy
REFERRER_POLICY=strict-origin-when-cross-origin
# Permissions-Policy
PERMISSIONS_POLICY_CAMERA="()" # Deny by default
PERMISSIONS_POLICY_MICROPHONE="()"
PERMISSIONS_POLICY_GEOLOCATION="()"
PERMISSIONS_POLICY_PAYMENT="()"
PERMISSIONS_POLICY_USB="()"After updating .env:
# Export headers to nginx config
nself security headers export
# Rebuild nginx configuration
nself build --force
# Restart nginx
nself restart nginx# Show current configuration
nself security headers show
# Interactive configuration wizard
nself security headers configure
# Validate headers from running server
nself security headers validate https://yourdomain.com
# Export to nginx configuration
nself security headers export [output-file]
# Generate security headers report
nself security headers report [output-file]# Show CSP configuration
nself security headers csp show
# Configure CSP interactively
nself security headers csp configure
# Add domain to CSP whitelist
nself security headers csp add-domain cdn.example.com
# Remove domain from whitelist
nself security headers csp remove-domain cdn.example.com
# List whitelisted domains
nself security headers csp list-domains
# Validate CSP syntax
nself security headers csp validate
# Export CSP to nginx format
nself security headers csp export [mode] [output-file]# Check headers
curl -I https://yourdomain.com
# Look for security headers
curl -I https://yourdomain.com | grep -E "Content-Security-Policy|Strict-Transport-Security|X-Frame-Options"# Validate all headers
nself security headers validate https://yourdomain.com
# Security checklist (includes headers)
nself security scanMozilla Observatory: https://observatory.mozilla.org/ Comprehensive security scanner including all headers
SecurityHeaders.com: https://securityheaders.com/ Quick check for security header presence and configuration
CSP Evaluator: https://csp-evaluator.withgoogle.com/ Google's CSP validator and security analyzer
SSL Labs: https://www.ssllabs.com/ssltest/ Includes HSTS and security header checks
- Open DevTools (F12)
- Go to Network tab
- Click any request
- Look at Response Headers
- Verify security headers are present
-
Always use HTTPS in production
SSL_ENABLED=true SSL_PROVIDER=letsencrypt
-
Enable strict CSP mode
CSP_MODE=strict
-
Use DENY for X-Frame-Options
X_FRAME_OPTIONS=DENY
-
Set HSTS max-age to 1 year
HSTS_MAX_AGE=31536000 HSTS_INCLUDE_SUBDOMAINS=true
-
Validate headers before deployment
nself security headers validate https://staging.yourdomain.com
-
Use moderate or permissive CSP
CSP_MODE=moderate # or permissive for development -
Allow localhost in CSP if needed
nself security headers csp add-domain localhost
-
Test headers locally
nself security headers validate http://localhost
Start permissive, gradually tighten:
# Week 1: Permissive
CSP_MODE=permissive
# Week 2: Moderate (monitor for issues)
CSP_MODE=moderate
# Week 3+: Strict (after fixing any issues)
CSP_MODE=strict- Use CSP Report-Only mode first (TODO: add to nself)
- Monitor browser console for CSP violations
- Set up CSP reporting endpoint (TODO: add to nself)
-
Run security audits regularly
nself security scan
Symptom: Scripts, styles, or images not loading
Solution:
- Check browser console for CSP violations
- Add allowed domains:
nself security headers csp add-domain cdn.example.com
- Or temporarily use permissive mode:
CSP_MODE=permissive nself build --force
Symptom: HSTS header not present
Causes:
- SSL not enabled:
SSL_ENABLED=truerequired - Not using HTTPS: HSTS only works on HTTPS
- Not rebuilt: Run
nself build --force
Solution:
# Ensure SSL is enabled
export SSL_ENABLED=true
# Rebuild configuration
nself build --force
# Restart nginx
nself restart nginx
# Test
nself security headers validate https://yourdomain.comSymptom: Inline JavaScript not executing
Causes: Strict CSP blocks unsafe-inline
Solutions:
- Best: Move scripts to external files
- Good: Use nonces (TODO: add to nself)
-
Temporary: Use moderate mode
CSP_MODE=moderate
Symptom: Analytics, chat widgets, or other third-party tools not working
Solution: Whitelist the required domains
# Example: Google Analytics
nself security headers csp add-domain www.google-analytics.com
nself security headers csp add-domain ssl.google-analytics.com
# Example: Intercom
nself security headers csp add-domain widget.intercom.io
nself security headers csp add-domain js.intercomcdn.com
# Rebuild
nself build --forceChecklist:
- ✅ Configuration generated:
ls nginx/includes/security-headers.conf - ✅ Included in nginx:
grep security-headers nginx/conf.d/default.conf - ✅ Nginx restarted:
nself restart nginx - ✅ Testing correct URL: HTTPS vs HTTP
Symptom: Site needs to be embedded in iframe
Solution: Use SAMEORIGIN instead of DENY
export X_FRAME_OPTIONS=SAMEORIGIN
nself security headers export
nself build --forceUse this checklist for production deployments:
- CSP configured and tested
- HSTS enabled with 1-year max-age
- X-Frame-Options set to DENY or SAMEORIGIN
- X-Content-Type-Options set to nosniff
- Referrer-Policy configured
- Permissions-Policy denies unused features
- All headers validated with
nself security headers validate - Online scan passed (Mozilla Observatory, SecurityHeaders.com)
- Headers present in production
- No console errors related to CSP
- Third-party integrations working
- Site functions correctly
- SSL Labs test passed (A rating)
- Monitor logs for CSP violations
- SSL/TLS Configuration
- Firewall Configuration
- Security Checklist
- Security Audit Report (see project documentation)
OWASP Secure Headers Project: https://owasp.org/www-project-secure-headers/
MDN Web Security: https://developer.mozilla.org/en-US/docs/Web/Security
Content Security Policy Reference: https://content-security-policy.com/
HSTS Preload List: https://hstspreload.org/
Version: nself v0.9.0 Sprint: 17 - Advanced Security Last Updated: January 2026