itecture decision: We include includeSubDomains to prevent attackers from exploiting less-secured subdomains as downgrade vectors. The max-age is set to 12 months to balance security with operational flexibility. Shorter durations increase vulnerability windows; longer durations complicate domain migration or certificate rotation. Note that the preload token is intentionally omitted here. Adding preload without registering your domain in browser vendor databases causes inconsistent enforcement across clients. Registration requires a separate submission process and a minimum 18-month commitment.
Phase 2: Content Parsing & Framing Controls
Browsers historically attempt to infer file types based on content rather than declared MIME types. This behavior, known as MIME sniffing, enables attackers to disguise malicious scripts as benign assets. The X-Content-Type-Options header disables this fallback mechanism.
export function lockContentParsing(req: Request, res: Response, next: NextFunction) {
res.setHeader('X-Content-Type-Options', 'nosniff');
next();
}
For framing restrictions, the legacy X-Frame-Options header prevents embedding your pages in external iframes. While DENY provides maximum protection, modern browsers prioritize the frame-ancestors directive within Content Security Policy. We deploy X-Frame-Options as a compatibility fallback while preparing the CSP transition. This dual-layer approach ensures older browsers receive protection while newer clients evaluate the more granular CSP directive.
Phase 3: Data Leakage Prevention
The Referer header automatically transmits the full source URL when users navigate to external domains. If your application routes contain session identifiers, user IDs, or internal API paths, this data leaks to third-party servers. The Referrer-Policy header restricts what information travels with outbound requests.
export function controlReferrerLeakage(req: Request, res: Response, next: NextFunction) {
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
next();
}
Rationale: strict-origin-when-cross-origin sends the full URL for same-origin navigation but strips the path for cross-origin requests. This preserves analytics functionality while eliminating sensitive path exposure. Alternative policies like no-referrer break legitimate tracking integrations, while unsafe-url defeats the purpose entirely. Teams should also audit application routing to ensure sensitive parameters are never exposed in GET requests or URL fragments.
Phase 4: Resource Isolation (CSP)
Content Security Policy defines an allowlist for executable resources. When properly configured, it neutralizes cross-site scripting attacks by preventing unauthorized script execution, even if injection occurs. Implementation requires mapping all external dependencies: analytics providers, font CDNs, payment gateways, and widget scripts.
export function deployContentPolicy(req: Request, res: Response, next: NextFunction) {
const cspDirectives = [
"default-src 'self'",
"script-src 'self' https://trusted-analytics.example.com",
"style-src 'self' https://fonts.example.com",
"img-src 'self' data: https://cdn.example.com",
"connect-src 'self' https://api.example.com",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'"
].join('; ');
// Report-only mode for validation
res.setHeader('Content-Security-Policy-Report-Only', cspDirectives);
next();
}
Architecture decision: We deploy in Report-Only mode initially. This captures violation events without blocking resources, allowing you to identify missing third-party origins. Once the report stream stabilizes, you switch to the enforcing Content-Security-Policy header. We use explicit domain allowlists instead of wildcards to maintain strict boundaries. Dynamic nonce generation is recommended for inline scripts, but requires framework-level integration and careful cache invalidation strategies. Hash-based CSP is preferable for static assets, while nonces suit dynamic server-rendered content.
Phase 5: Outbound Identity Verification
HTTP headers protect inbound traffic, but domain reputation requires DNS-level authentication. SPF (Sender Policy Framework) and DMARC (Domain-based Message Authentication, Reporting, and Conformance) prevent attackers from spoofing your domain in phishing campaigns. These records are configured at the DNS provider, not in application code. Your email service provider supplies the exact TXT record values. Without them, receiving mail servers cannot verify message authenticity, leaving your brand vulnerable to credential harvesting attacks. DMARC should be deployed incrementally: start with p=none to collect forensic reports, validate legitimate mail flows, then escalate to p=quarantine and finally p=reject.
Pitfall Guide
-
HSTS Preload Without Registration
Explanation: Adding the preload token without submitting your domain to browser preload lists causes inconsistent enforcement across clients. Some browsers will ignore the directive, creating a false sense of security.
Fix: Remove preload from the header until your domain is accepted into the Chromium/HSTS preload database. Verify status via official browser vendor portals and maintain a 12-month minimum max-age before submission.
-
Enforcing CSP Before Dependency Mapping
Explanation: Switching from Report-Only to enforcing mode prematurely blocks legitimate third-party scripts, breaking analytics, payment flows, or UI components. Silent failures in production are difficult to trace without proper logging.
Fix: Maintain report-only mode for at least 7β14 days. Aggregate violation reports, update the allowlist, and validate in staging before enforcing. Implement a dedicated endpoint to receive and parse CSP violation JSON payloads.
-
Ignoring Subdomain Scope in HSTS
Explanation: Omitting includeSubDomains leaves development, staging, or legacy subdomains vulnerable to downgrade attacks that can compromise main domain cookies or session tokens.
Fix: Always include includeSubDomains unless you explicitly manage separate security policies per subdomain. Document exceptions in infrastructure runbooks and ensure all public-facing subdomains enforce HTTPS independently.
-
Relying Solely on X-Frame-Options
Explanation: Modern browsers deprecate X-Frame-Options in favor of CSP frame-ancestors. Using only the legacy header leaves gaps in newer browser versions and prevents granular origin control.
Fix: Deploy both headers during transition. Prioritize frame-ancestors 'none' in CSP and remove X-Frame-Options once legacy browser support drops below operational thresholds. Use frame-ancestors to permit specific trusted embeds when business requirements demand it.
-
Leaking Internal Paths via Default Referrer Behavior
Explanation: Browsers default to sending full URLs on cross-origin navigation. Internal routing structures, user identifiers, or API keys embedded in URLs become visible to external domains, enabling reconnaissance and session fixation.
Fix: Implement strict-origin-when-cross-origin as the baseline. Audit application URLs for sensitive parameters and migrate them to POST bodies, secure cookies, or server-side session storage.
-
Skipping Email Authentication Records
Explanation: Focusing exclusively on HTTP headers while ignoring SPF and DMARC leaves your domain exposed to phishing impersonation, damaging brand trust and email deliverability rates.
Fix: Coordinate with your email infrastructure team to publish SPF, DKIM, and DMARC records. Start with monitoring mode, validate aggregate reports, then enforce rejection policies. Treat DNS records as critical security infrastructure, not optional compliance items.
-
Caching Security Headers Incorrectly
Explanation: Placing security headers behind aggressive CDN caching without proper cache-control directives can cause stale policies to persist after updates. Edge nodes may serve outdated CSP or HSTS values to new visitors.
Fix: Ensure security headers are either served directly from the origin or configured with Cache-Control: no-store for header-specific responses. Validate CDN edge behavior in staging and implement header versioning if policy rotation is frequent.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Legacy monolith with unknown third-party scripts | CSP Report-Only + gradual allowlist expansion | Prevents immediate breakage while identifying dependencies | Low engineering cost, moderate monitoring overhead |
| Modern SPA with strict build pipeline | CSP enforcing with nonces/hashes | Eliminates inline script risks and enforces deterministic resource loading | Higher initial setup, lower long-term maintenance |
| Multi-tenant platform with customer subdomains | HSTS without includeSubDomains + per-tenant policies | Prevents cross-tenant policy leakage while maintaining isolation | Moderate infrastructure complexity, high security ROI |
| Marketing site with heavy analytics/widget usage | Referrer-Policy strict-origin + CSP allowlist for known vendors | Balances tracking functionality with data leakage prevention | Low cost, requires vendor coordination |
Configuration Template
# Production-ready reverse proxy security header configuration
# Apply to all public-facing server blocks
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "DENY" always;
# CSP deployed in report-only mode during validation phase
add_header Content-Security-Policy-Report-Only \
"default-src 'self'; \
script-src 'self' https://analytics.vendor.com; \
style-src 'self' https://fonts.vendor.com; \
img-src 'self' data: https://cdn.vendor.com; \
connect-src 'self' https://api.internal.com; \
frame-ancestors 'none'; \
base-uri 'self'; \
form-action 'self'; \
report-uri /csp-violation-endpoint;" always;
# Ensure headers are not stripped by proxy intermediaries
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
Quick Start Guide
- Inject baseline headers: Add HSTS,
X-Content-Type-Options, and Referrer-Policy to your reverse proxy or application middleware. Restart the service and verify response headers in browser developer tools.
- Validate transport enforcement: Navigate to your site over HTTP and confirm automatic upgrade to HTTPS. Check that
Strict-Transport-Security appears in the response and that mixed-content warnings are resolved.
- Deploy CSP in report-only mode: Copy the template policy, replace vendor domains with your actual dependencies, and apply the
Content-Security-Policy-Report-Only header. Monitor the console for violation warnings.
- Aggregate and refine: Set up a lightweight endpoint to receive CSP reports or use browser console warnings. Log missing origins for 7β14 days, update the allowlist, and validate in staging.
- Enforce and harden: Update the header to
Content-Security-Policy, remove the report-only suffix, and verify third-party integrations function correctly in production. Document the policy in your infrastructure repository for version control.