While debugging a ModSecurity implementation, I encountered an unexpected behavior where requests without the optional ACCEPT header were being blocked. The CRS rule 960015 triggered with a CRITICAL severity, treating this as a protocol violation.
The ModSecurity Core Rule Set (CRS) enforces this requirement primarily because:
- Legitimate browsers and API clients always include ACCEPT headers
- Missing headers often indicate primitive bots or vulnerability scanners
- It helps detect HTTP smuggling attempts
- Many web frameworks expect ACCEPT headers for proper content negotiation
There are valid cases where you might want to disable this rule:
# Example of disabling the rule in modsecurity.conf
SecRuleRemoveById 960015
Or for specific endpoints:
# In your VirtualHost configuration
<Location "/api/special-endpoint">
SecRuleRemoveById 960015
</Location>
Instead of completely disabling the rule, consider these approaches:
# 1. Set a default ACCEPT header in Apache
RequestHeader set Accept "text/html" "expr=%{REQUEST_HEADERS:Accept} == ''"
# 2. Modify the rule to be less strict
SecRuleUpdateTargetById 960015 !REQUEST_HEADERS:Accept
Use curl to verify your changes:
curl -v http://yoursite.com -H "Accept: "
curl -v http://yoursite.com/api/special-endpoint
Before disabling this protection, ensure:
- Your application doesn't rely on content negotiation
- You have other security measures in place
- You're not exposing sensitive endpoints
Remember that security headers like ACCEPT help create a more predictable request profile that's easier to monitor for anomalies.
When working with mod_security's Core Rule Set (CRS), many developers encounter unexpected 400 errors due to missing Accept headers. While HTTP/1.1 technically makes this header optional, security frameworks often enforce stricter requirements.
The CRS rule 960015 checks for missing Accept headers because:
- Legitimate browsers and API clients always include Accept headers
- Many automated attacks omit standard headers to reduce fingerprinting
- Absence of Accept headers often correlates with vulnerability scanners
- It helps detect non-browser traffic attempting to mimic HTTP
Here's the relevant portion from modsecurity_crs_21_protocol_anomalies.conf:
SecRule REQUEST_HEADERS:Accept "@validateByteRange 32-126" \ "phase:2,t:none,block,msg:'Request Missing an Accept Header',\ id:960015,severity:'CRITICAL',tag:'PROTOCOL_VIOLATION/MISSING_HEADER'"
While generally recommended, you might need to bypass this rule for:
- Legacy systems with custom clients
- IoT devices with limited HTTP stack
- Specific API endpoints with custom requirements
To disable the rule in your configuration:
SecRuleRemoveById 960015
For developers creating HTTP clients that need to work with mod_security:
// JavaScript fetch example fetch('https://api.example.com', { headers: { 'Accept': '*/*', // Wildcard accepts all content types 'Content-Type': 'application/json' } }); # Python requests example import requests headers = {'Accept': 'application/json'} response = requests.get('https://api.example.com', headers=headers)
Instead of completely disabling the rule, consider these approaches:
- Update your rule to allow empty Accept headers for specific paths:
SecRule REQUEST_URI "@beginsWith /api/v1/legacy" \ "phase:1,id:1001,nolog,pass,ctl:ruleRemoveById=960015"
- Set a default Accept header at the reverse proxy level
- Use mod_security's anomaly scoring instead of immediate blocking
While the security benefits are clear, be aware that:
- Header validation adds minimal overhead (typically < 0.1ms per request)
- False positives can occur with legitimate low-level HTTP clients
- The rule only triggers during the second phase of processing