Security headers provide an essential layer of protection for your React applications by instructing browsers how to handle your content and what security policies to enforce. Here’s a comprehensive guide to implementing them effectively:
Essential Security Headers
1. Content Security Policy (CSP)
// In your server configuration (Express.js example)
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "trusted.cdn.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "trusted.storage.com"],
connectSrc: ["'self'", "api.yourdomain.com"],
frameSrc: ["'none'"],
fontSrc: ["'self'", "fonts.gstatic.com"],
objectSrc: ["'none'"]
}
})
);
2. HTTP Strict Transport Security (HSTS)
app.use(
helmet.hsts({
maxAge: 63072000, // 2 years in seconds
includeSubDomains: true,
preload: true
})
);
3. X-Content-Type-Options
app.use(helmet.noSniff()); // Sets "X-Content-Type-Options: nosniff"
Implementation Methods
1. Server-Side Configuration (Recommended)
Express.js
const express = require('express');
const helmet = require('helmet');
const app = express();
// Apply all security headers
app.use(helmet());
// Customize specific headers
app.use(
helmet.contentSecurityPolicy({
/* your CSP config */
})
);
Nginx
server {
add_header X-Frame-Options "DENY";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Permissions-Policy "geolocation=(), microphone=()";
}
2. Static File Approach (for SPAs)
meta tags in public/index.html
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<meta http-equiv="X-Content-Type-Options" content="nosniff">
<meta http-equiv="X-Frame-Options" content="DENY">
3. Netlify/_headers File
/*
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=()
React-Specific Considerations
1. Development vs Production CSP
// webpack.config.js
const isProd = process.env.NODE_ENV === 'production';
new HtmlWebpackPlugin({
meta: {
'Content-Security-Policy': {
'http-equiv': 'Content-Security-Policy',
'content': isProd
? "default-src 'self'; script-src 'self' 'unsafe-inline' cdn.example.com"
: "default-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self' ws://localhost:*"
}
}
});
2. Handling Inline Scripts and Styles
// For React's required inline scripts, use nonces or hashes
const nonce = Buffer.from(crypto.randomBytes(16)).toString('base64');
app.use((req, res, next) => {
res.locals.cspNonce = nonce;
next();
});
// In your CSP config
scriptSrc: [`'self'`, `'nonce-${nonce}'`]
Advanced Header Configurations
1. Feature Policy (Permissions Policy)
app.use(
helmet.permittedCrossDomainPolicies({
permittedPolicies: 'none'
})
);
// Modern browsers use Permissions-Policy
app.use((req, res, next) => {
res.setHeader(
'Permissions-Policy',
'geolocation=(), microphone=(), camera=(), payment=()'
);
next();
});
2. Referrer Policy
app.use(helmet.referrerPolicy({ policy: 'strict-origin-when-cross-origin' }));
3. Expect-CT Header (for Certificate Transparency)
app.use(
helmet.expectCt({
maxAge: 86400,
enforce: true,
reportUri: 'https://yourdomain.com/report-ct'
})
);
Testing Your Headers
1. Manual Verification
curl -I https://yourdomain.com
2. Automated Testing
// Jest test example
test('should have security headers', async () => {
const response = await fetch('http://localhost:3000');
expect(response.headers.get('X-Frame-Options')).toBe('DENY');
expect(response.headers.get('X-Content-Type-Options')).toBe('nosniff');
expect(response.headers.get('Content-Security-Policy')).toContain("default-src 'self'");
});
3. Online Tools
Troubleshooting Common Issues
1. Development Environment Problems
// webpack-dev-server configuration
devServer: {
headers: {
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY'
}
}
2. CDN and Third-Party Resources
// Allow specific CDNs in CSP
scriptSrc: [
"'self'",
"https://cdn.jsdelivr.net",
"https://unpkg.com"
]
3. WebSocket Connections
// For development with HMR
connectSrc: [
"'self'",
"ws://localhost:3000",
"wss://yourdomain.com"
]
Best Practices Checklist
- [ ] Implement all recommended security headers
- [ ] Configure CSP with minimal required permissions
- [ ] Enable HSTS with preload option
- [ ] Disable MIME type sniffing
- [ ] Set strict referrer policy
- [ ] Restrict feature permissions
- [ ] Test headers in all environments
- [ ] Monitor for header-related errors
- [ ] Document your security header policy
- [ ] Regularly review and update headers
Maintenance and Monitoring
- Automated Header Validation
# GitHub Actions example
- name: Check security headers
uses: treosh/example-linter@v1
with:
url: https://yourdomain.com
headers:
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
- CSP Violation Reporting
// In your CSP config
directives: {
reportUri: '/csp-violation-report-endpoint'
}
// Then handle reports
app.post('/csp-violation-report-endpoint', (req, res) => {
logger.warn('CSP violation', req.body);
res.status(204).end();
});
- Regular Security Audits
# Use security scanning tools
npx lighthouse https://yourdomain.com --view --output=json