Skip to main content

ModSecurity WAF: protecting web applications on Apache/Nginx

Security · 19.04.2026 · 7 min read
Illustration for “ModSecurity WAF: protecting web applications on Apache/Nginx”

ModSecurity is an open-source Web Application Firewall (WAF) that runs as an Apache or Nginx module. It analyzes HTTP traffic in real time and blocks SQL injection, XSS, RFI, and other attacks.

Installing ModSecurity on Ubuntu/Apache

sudo apt update
sudo apt install -y libapache2-mod-security2

# Enable the module
sudo a2enmod security2
sudo systemctl restart apache2

# Check
apachectl -M | grep security

Basic configuration

# Copy the config
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf

# Change the mode from DetectionOnly to On
sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf

Installing OWASP CRS (Core Rule Set)

sudo apt install -y modsecurity-crs

# Or manually:
cd /etc/modsecurity
sudo git clone https://github.com/coreruleset/coreruleset.git
cd coreruleset
sudo cp crs-setup.conf.example crs-setup.conf
✅ OWASP CRS is a standard rule set that protects against the OWASP Top 10 attacks. Update it regularly.

Operating modes

ModeConfigAction
Detection OnlySecRuleEngine DetectionOnlyLogs, doesn't block
Active (On)SecRuleEngine OnBlocks requests
OffSecRuleEngine OffCompletely disabled

Managing false positives

# Disable the rule for a specific URL
SecRule REQUEST_URI "@beginsWith /wp-admin/post.php"     "id:1000001,phase:1,pass,nolog,ctl:ruleRemoveById=941100"

# Disable the rule globally (not recommended)
SecRuleRemoveById 941100
⚠️ Start with DetectionOnly mode for 2–4 weeks, study the logs, and only then switch to On. Otherwise you risk blocking legitimate users.

ModSecurity logs

# View blocked requests
sudo tail -f /var/log/apache2/modsec_audit.log | grep "HTTP 403"

# Attack statistics
sudo grep "OWASP" /var/log/apache2/modsec_audit.log | awk '{print $NF}' | sort | uniq -c | sort -rn

Real ModSecurity messages explained

  • AH00526: Syntax error on line X ...: Invalid command 'SecRule', perhaps misspelled or defined by a module not included in the server configuration — this is exactly what apachectl configtest reports when rule files are included but the security2 module itself is not enabled, or the server was never restarted after a2enmod.
  • ModSecurity: Access denied with code 403 (phase 2). Matched "Operator ... to variable ARGS:..." [id "941100"] [msg "XSS Attack Detected via libinjection"] — a typical audit log line when a rule fires; the id value is the specific rule number used to disable just that one rule with ctl:ruleRemoveById.
  • ModSecurity: Warning. ... [id "200002"] [msg "Failed to parse request body."] — a common false positive during a multipart file upload, usually when the request body exceeds SecRequestBodyLimit or uses a nonstandard boundary.
  • ModSecurity: Rule ... execution error - PCRE limit exceeded — a regular expression from one of the CRS rules did not finish matching against an overly long field value; fixed by raising SecPcreMatchLimit or limiting the field's length at the application level.
  • A rule is disabled with SecRuleEngine Off inside one specific <VirtualHost>, while the global config has On — Apache applies the more specific directive, so that one site stays unprotected while logs for every other domain show the WAF working normally.

OWASP CRS paranoia levels

Paranoia LevelWhat it coversFalse positivesRecommendation
PL1Basic rules against the most common attacks: SQL injection and XSS with clear signaturesMinimalThe starting level for most sites
PL2Adds broader heuristics and checks for nonstandard encodingsOccasional, on unusual formsFor sites with higher security requirements
PL3Strict checks, many additional patternsFrequent, needs manual exception tuningOnly after weeks of testing in DetectionOnly mode
PL4The most conservative rule set availableVery frequent on any nonstandard inputSpecialized environments like payments or government, almost always with manual exceptions

When ModSecurity is not the right fit

  • A site with a millisecond response budget and very high load — inspecting every request against every CRS rule adds noticeable latency; such projects more often put a WAF at the edge (such as Cloudflare) instead of on every backend server.
  • A project under active development with forms that change often — at a paranoia level above PL1, you end up regularly triaging new false positives instead of configuring the rules once and moving on.
  • Shared hosting with no root access, only PHP over FTP — ModSecurity runs at the web server level and needs administrative access; on shared hosting without that, it cannot be enabled, and only the provider's built-in WAF, if any, is available.
  • Protection against business-logic attacks — tampering with an order's price, bypassing paid content through requests that are technically well-formed — ModSecurity looks for signs of known attacks in the request itself, not application logic; that needs code on the application side, not WAF rules.

Verifying that the WAF actually blocks attacks

# Confirm the module is actually loaded, not just present in the config
apachectl -M | grep security

# Send a harmless test payload and confirm the WAF catches it
curl "https://example.com/?test=<script>alert(1)</script>"
# A 403 response is expected if the XSS rule is active

# Check which exact rule fired
sudo tail -20 /var/log/apache2/modsec_audit.log | grep -E "id \"|msg "

If curl returns a normal page instead of 403, either the engine is in DetectionOnly mode, or the specific XSS rule is disabled or overridden at the virtual host level; in that case, go back to the first item in this section.

Frequently asked questions

Does ModSecurity replace antivirus or malware scanning on the server?

No, these are different jobs. ModSecurity analyzes incoming HTTP requests in real time and looks for signs of known attacks in the request itself, not the content of files on disk. Finding malicious scripts or web shells already uploaded to the server requires a separate filesystem antivirus scanner — ModSecurity does not replace that and does not perform that function.

If a site already sits behind Cloudflare's WAF, is ModSecurity on the server still needed?

That is sensible redundancy, not overkill. Cloudflare filters traffic at the network edge and catches most mass attacks before they reach the server, but it does not protect against requests that hit the server's IP directly, bypassing the proxy. ModSecurity on the server remains the last line of defense for exactly that kind of bypass.

Can ModSecurity rules be set per individual site rather than as one shared set for the whole server?

Yes, rules can be set both globally for the whole server and inside the configuration of a specific VirtualHost — a more specific setting in Apache always overrides the general one. This is useful when one site on the server needs a stricter level of checking while another regularly triggers false positives on the same rule set.

Does enabling ModSecurity noticeably slow down the site for regular visitors?

There is some slowdown, but on most sites it goes unnoticed: the rules check a request in a fraction of a second. Delay becomes noticeable only on very high-traffic projects or with the strictest Paranoia level enabled, adding dozens of extra checks per request — then a WAF is placed at the network edge rather than on every backend.

Was this article helpful?
← Back to Knowledge Base Ask Support