- Home
- HTTP Headers
- X-XSS-Protection Header
Header
X-XSS-Protection Header
Deprecated header that enabled browser XSS filters to detect and block reflected cross-site scripting attacks.
TL;DR: Deprecated header that controlled browser XSS filters. Don’t use it—set to
0to disable legacy filters and use Content-Security-Policy for XSS protection instead.
What is X-XSS-Protection?
The X-XSS-Protection header was a security feature that enabled built-in cross-site scripting (XSS) filters in older browsers. It instructed browsers to detect and block pages when reflected XSS attacks were detected.
Important: This header is now deprecated and should not be used. Modern browsers have removed XSS auditors due to security issues, and Content-Security-Policy (CSP) is the recommended approach for XSS protection.
How X-XSS-Protection Worked
Enable XSS filter:
HTTP/1.1 200 OK
Content-Type: text/html
X-XSS-Protection: 1; mode=block
<!DOCTYPE html>
<html>...
```http
Browser would block page if XSS was detected.
## Syntax
```http
X-XSS-Protection: 0
X-XSS-Protection: 1
X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; report=<reporting-uri>
Directives
- 0 - Disable XSS filter
- 1 - Enable XSS filter (sanitize page)
- 1; mode=block - Enable filter and block page entirely
- 1; report=
- Enable filter and report violation (Chromium only)
Historical Usage
Disable Filter
X-XSS-Protection: 0
```text
Turned off XSS filtering (useful when it caused false positives).
### Enable with Sanitization
```http
X-XSS-Protection: 1
Browser would attempt to sanitize malicious code.
Enable with Blocking
X-XSS-Protection: 1; mode=block
```text
Browser would block the entire page instead of sanitizing.
### Enable with Reporting
```http
X-XSS-Protection: 1; mode=block; report=/xss-report
Block page and send violation report (Chromium-only feature).
Why It’s Deprecated
1. Created New Vulnerabilities
XSS auditors themselves introduced security vulnerabilities by allowing attackers to:
- Selectively disable legitimate scripts
- Create new attack vectors
- Bypass security measures
2. False Positives
XSS filters frequently blocked legitimate content:
// Legitimate code that might trigger filter
const url = window.location.href
document.write('<div>' + url + '</div>')
```text
### 3. Inconsistent Behavior
Different browsers implemented XSS filters differently, causing:
- Unpredictable behavior
- Difficult debugging
- Cross-browser compatibility issues
### 4. Better Alternative Exists
Content-Security-Policy (CSP) provides:
- More granular control
- Better protection
- No false positive issues
- Active development and support
## Browser Support Timeline
### Chrome/Chromium
```text
Chrome 78+ (2019): XSS Auditor removed
Chrome 4-77: Supported
```text
### Firefox
```http
Firefox: Never implemented XSS Auditor
```text
### Safari
```http
Safari: Disabled by default in recent versions
```text
### Edge
```text
Legacy Edge: Supported
Chromium Edge 79+: XSS Auditor removed
```text
### Internet Explorer
```text
IE 8-11: Supported
```text
## Migration to CSP
### Old Approach (Deprecated)
```http
X-XSS-Protection: 1; mode=block
New Approach (Recommended)
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{random}'; object-src 'none'
```text
CSP provides comprehensive XSS protection without the issues of XSS auditors.
## Server Configuration
### What NOT to Do Anymore
```nginx
# ❌ Don't add X-XSS-Protection header
add_header X-XSS-Protection "1; mode=block";
What to Do Instead
# ✅ Use Content-Security-Policy
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'";
# Optionally include X-XSS-Protection: 0 to ensure it's disabled
add_header X-XSS-Protection "0";
```javascript
## Removing X-XSS-Protection
### Node.js (Express)
```javascript
const express = require('express')
const helmet = require('helmet')
const app = express()
// Modern approach with helmet
app.use(
helmet({
// Helmet v5+ doesn't set X-XSS-Protection by default
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
objectSrc: ["'none'"]
}
}
})
)
// If using older helmet, explicitly disable
app.use((req, res, next) => {
res.removeHeader('X-XSS-Protection')
next()
})
app.listen(3000)
Legacy Code Cleanup
// ❌ Old code - remove this
app.use((req, res, next) => {
res.set('X-XSS-Protection', '1; mode=block')
next()
})
// ✅ Replace with CSP
app.use((req, res, next) => {
res.set('Content-Security-Policy', "default-src 'self'; script-src 'self'")
next()
})
```text
### Nginx
```nginx
# ❌ Remove old configuration
# add_header X-XSS-Protection "1; mode=block";
# ✅ Use CSP instead
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'";
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY";
Apache
# ❌ Remove old configuration
# Header set X-XSS-Protection "1; mode=block"
# ✅ Use CSP instead
Header set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'"
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "DENY"
```text
## Modern XSS Protection
### 1. Content-Security-Policy (Primary Defense)
```http
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{random}';
style-src 'self' 'nonce-{random}';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none'
2. Input Validation and Sanitization
// Validate and sanitize user input
const sanitizeHtml = require('sanitize-html')
app.post('/comment', (req, res) => {
const clean = sanitizeHtml(req.body.comment, {
allowedTags: ['b', 'i', 'em', 'strong'],
allowedAttributes: {}
})
db.saveComment(clean)
res.json({ success: true })
})
```javascript
### 3. Output Encoding
```javascript
// Use template engines with auto-escaping
const handlebars = require('handlebars')
// Handlebars auto-escapes by default
const template = handlebars.compile('<div>{{userInput}}</div>')
const output = template({ userInput: '<script>alert("XSS")</script>' })
// Output: <div><script>alert("XSS")</script></div>
4. HttpOnly Cookies
// Prevent JavaScript access to cookies
res.cookie('session', token, {
httpOnly: true,
secure: true,
sameSite: 'strict'
})
```text
### 5. Trusted Types
```http
Content-Security-Policy: require-trusted-types-for 'script'
// Use Trusted Types API
if (window.trustedTypes && trustedTypes.createPolicy) {
const policy = trustedTypes.createPolicy('default', {
createHTML: (string) => sanitizeHtml(string)
})
element.innerHTML = policy.createHTML(userInput)
}
```javascript
## Complete Security Headers Setup
### Modern Security Configuration
```javascript
const helmet = require('helmet')
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'nonce-{random}'"],
styleSrc: ["'self'", "'nonce-{random}'"],
imgSrc: ["'self'", 'data:', 'https:'],
objectSrc: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"],
frameAncestors: ["'none'"],
upgradeInsecureRequests: []
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
},
frameguard: {
action: 'deny'
},
noSniff: true,
referrerPolicy: {
policy: 'strict-origin-when-cross-origin'
}
})
)
Nginx Configuration
# Modern security headers
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Explicitly disable XSS Protection (optional)
add_header X-XSS-Protection "0" always;
```text
## Testing and Validation
### Check Headers
```bash
# Check current headers
curl -I https://example.com
# Should NOT see X-XSS-Protection (or see it set to 0)
# SHOULD see Content-Security-Policy
Security Scanners
# Use online tools
# - https://securityheaders.com
# - https://observatory.mozilla.org
# Or CLI tools
npm install -g observatory-cli
observatory example.com
```javascript
### Browser DevTools
```javascript
// Check CSP in Console
console.log(document.querySelector("meta[http-equiv='Content-Security-Policy']"))
// Or check response headers in Network tab
fetch('/api/data').then((res) => {
console.log('CSP:', res.headers.get('Content-Security-Policy'))
console.log('X-XSS-Protection:', res.headers.get('X-XSS-Protection') || 'Not set')
})
Best Practices
1. Remove X-XSS-Protection
// ✅ Don't set it at all, or explicitly disable
res.set('X-XSS-Protection', '0')
// ❌ Don't enable it
res.set('X-XSS-Protection', '1; mode=block')
```text
### 2. Implement CSP
```http
# ✅ Comprehensive CSP
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123'
# ❌ Relying on deprecated header
X-XSS-Protection: 1; mode=block
3. Use Multiple Security Layers
// Defense in depth
app.use(helmet()) // Security headers
app.use(sanitizeInput) // Input sanitization
app.use(csurf()) // CSRF protection
app.use(rateLimit()) // Rate limiting
```text
### 4. Keep Dependencies Updated
```bash
# Regular security audits
npm audit
npm audit fix
# Update security packages
npm update helmet sanitize-html
5. Educate Developers
// Document why X-XSS-Protection is not used
/**
* Security Headers
*
* We do NOT use X-XSS-Protection because:
* - It's deprecated and removed from modern browsers
* - It created new vulnerabilities
* - Content-Security-Policy is the modern replacement
*
* Instead, we use:
* - Content-Security-Policy for XSS protection
* - Input validation and sanitization
* - Output encoding
* - HttpOnly cookies
*/
```text
## Common Misconceptions
### Myth 1: "Still Needed for Old Browsers"
**Reality:** Old browsers with XSS auditors have other security issues. Better to use CSP which degrades gracefully.
### Myth 2: "Setting it Doesn't Hurt"
**Reality:** XSS auditors can create vulnerabilities. If present in older browsers, explicitly disable with `X-XSS-Protection: 0`.
### Myth 3: "CSP is Too Complex"
**Reality:** Start with a simple CSP and iterate:
```http
# Simple starting point
Content-Security-Policy: default-src 'self'; script-src 'self'
Related Headers
- Content-Security-Policy - Modern XSS protection (use this instead)
- X-Content-Type-Options - MIME sniffing protection
- X-Frame-Options - Clickjacking protection
Frequently Asked Questions
What is X-XSS-Protection?
X-XSS-Protection controlled browser XSS filters. It is now deprecated because the filters could be exploited and CSP provides better protection.
Should I still use X-XSS-Protection?
Set X-XSS-Protection: 0 to disable legacy XSS filters that can cause issues. Use Content-Security-Policy instead for XSS protection.
Why was X-XSS-Protection deprecated?
The XSS filters had bypasses and could be exploited to disable legitimate scripts. Modern browsers removed the feature. CSP is the recommended replacement.
What should I use instead?
Use Content-Security-Policy with strict script-src directives. CSP provides comprehensive XSS protection without the vulnerabilities of X-XSS-Protection.