Secure Headers: HTTP Security Headers for Hardening Your Backend
In this tutorial, you will learn about Secure Headers: HTTP Security Headers for Hardening Your Backend. We cover key concepts, practical examples, and best practices to help you master this topic.
HTTP security headers instruct browsers how to behave when interacting with your application. They enable critical security features: forcing HTTPS, preventing clickjacking, blocking MIME type sniffing, controlling referrer information, and restricting browser features.
flowchart LR
Browser -->|Request| Server
Server -->|Response + Security Headers| Browser
Browser -->|Enforce Policies| User
subgraph Headers
HSTS[Strict-Transport-Security]
XFO[X-Frame-Options]
XCTO[X-Content-Type-Options]
RP[Referrer-Policy]
PP[Permissions-Policy]
CSP[Content-Security-Policy]
end
HSTS -->|Force HTTPS| Browser
XFO -->|Block iframe embedding| Browser
XCTO -->|Prevent MIME sniffing| Browser
RP -->|Control referrer| Browser
PP -->|Restrict browser APIs| Browser
CSP -->|Allowlisted sources| Browser
What You'll Learn
- Every critical HTTP security header and its purpose
- Configuring Helmet for Express-based applications
- HSTS preloading and upgrade-insecure-requests
- Permissions-Policy for granular API control
Why It Matters
Security headers are a high-impact, low-effort security improvement. Setting a few headers prevents entire categories of attacks: HSTS prevents SSL stripping, X-Frame-Options prevents clickjacking, and Permissions-Policy limits damage from XSS.
Real-World Use
A government services portal achieved an A+ security rating by implementing: HSTS with preload (max-age=31536000), X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, and Permissions-Policy: geolocation=().
Security Headers Implementation
Using Helmet for Express
const helmet = require('helmet');
app.use(helmet());
// Custom configuration
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.example.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https://images.example.com"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
frameAncestors: ["'none'"],
upgradeInsecureRequests: []
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
},
referrerPolicy: {
policy: 'strict-origin-when-cross-origin'
}
}));
Expected output:
Response headers include: Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Content-Security-Policy, Permissions-Policy.
Manual Security Header Configuration
app.use((req, res, next) => {
// HSTS: Force HTTPS for 1 year, include subdomains, preload
res.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
// Prevent clickjacking
res.set('X-Frame-Options', 'DENY');
// Prevent MIME type sniffing
res.set('X-Content-Type-Options', 'nosniff');
// Control referrer information
res.set('Referrer-Policy', 'strict-origin-when-cross-origin');
// Restrict browser features
res.set('Permissions-Policy', 'geolocation=(), microphone=(), camera=(), payment=()');
// Disable caching for sensitive data
res.set('Cache-Control', 'no-store, max-age=0');
next();
});
Expected output:
All responses include these security headers. Browsers enforce the specified policies, blocking unsafe behaviors.
Cross-Origin Isolation Headers
// For enabling SharedArrayBuffer and high-resolution timers
app.use('/api/secure', (req, res, next) => {
res.set('Cross-Origin-Opener-Policy', 'same-origin');
res.set('Cross-Origin-Embedder-Policy', 'require-corp');
res.set('Cross-Origin-Resource-Policy', 'same-origin');
next();
});
// For embedding in specific origins
app.use('/api/embed', (req, res, next) => {
res.set('Cross-Origin-Resource-Policy', 'cross-origin');
res.set('Access-Control-Allow-Origin', 'https://trusted-partner.com');
next();
});
Expected output:
Cross-Origin-Opener-Policy: same-origin isolates the browsing context group. Cross-Origin-Embedder-Policy: require-corp prevents loading cross-origin resources without explicit permission.
Common Mistakes
- Setting HSTS with short max-age (e.g., 3600), which provides no meaningful protection.
- Using X-Frame-Options: SAMEORIGIN when DENY is appropriate — only allow framing if explicitly needed.
- Forgetting to set X-Content-Type-Options: nosniff, allowing browsers to MIME-sniff and potentially execute uploaded files as scripts.
- Setting Permissions-Policy permissions too broadly (e.g., geolocation=(*)), allowing all origins to access sensitive APIs.
- Not including upgrade-insecure-requests in CSP, allowing mixed content on HTTPS pages.
Practice Questions
- What does Strict-Transport-Security do?
- How does X-Frame-Options prevent clickjacking?
- What is MIME type sniffing and why should you disable it?
- How does Referrer-Policy protect user privacy?
- What is Permissions-Policy and when would you use it?
Challenge
Set up an Express server with comprehensive security headers. Use Helmet with custom configuration. Add middleware that sets HSTS with preload for production but disables it for localhost. Test all headers with curl and verify with securityheaders.com.
FAQ
Mini Project
Build a security headers configuration module that produces different header sets for development, staging, and production. Production should include HSTS preload, strict CSP, and all protections. Staging includes relaxed CSP for debugging. Development is permissive. Test each environment.
What's Next
Continue to Rate Limiting to learn how to protect your APIs from abuse and DDoS attacks.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro