NGINX + ModSecurity v3 + OWASP CRS on Ubuntu 24.04 LTS – Step by Step – Part 3

Part 3 – Defining Proper Exceptions for WordPress

After successfully integrating and testing ModSecurity with the OWASP Core Rule Set (CRS), you’ll quickly run into a common issue when using dynamic applications like WordPress: false positives — legitimate requests incorrectly classified as attacks and blocked.

WordPress uses modern features like the REST API, AJAX, dynamic block editors (Gutenberg), and sometimes specific HTML comments. These mechanisms often trigger security rules when CRS is active — particularly in the areas of XSS, LFI, or anomaly detection. To ensure that these protections don’t interfere with the admin interface, we need to define targeted rule exceptions for WordPress.

How to Properly Configure WordPress Exceptions

To ensure these exceptions apply only to WordPress sites, we place them in a separate file and include it specifically in the nginx.conf or the corresponding VirtualHost configuration.

1. Create the Exception File

Create (or edit) the following file:

Bash
sudo nano /etc/nginx/modsec/wp-admin-exceptions.conf

And insert the following rules:

wp-admin-exceptions.conf
# WordPress-specific ModSecurity exceptions to reduce false positives

# Disable ModSecurity for admin/editor and REST API requests (to reduce false positives and speed up backend)
SecRule REQUEST_URI "@rx ^/(wp-admin|wp-json|wp-includes|wp-content|wp-cron\.php)" \
    "id:1000990,phase:1,pass,nolog,noauditlog,ctl:auditEngine=Off,ctl:requestBodyAccess=Off"

# Global rule to disable audit logging for all WordPress requests
SecRule REQUEST_URI "@rx ^/wp-(admin|json|includes|content)" "id:1001000,phase:1,pass,nolog,noauditlog,ctl:auditEngine=Off"

# Allow REST API (used by Gutenberg and AJAX calls)
SecRule REQUEST_URI "@beginsWith /wp-json/" "id:1001001,phase:1,pass,nolog,noauditlog,ctl:auditEngine=Off,\
  ctl:ruleRemoveById=941180,\
  ctl:ruleRemoveById=920450,\
  ctl:ruleRemoveById=949110,\
  ctl:ruleRemoveById=920100,\
  ctl:ruleRemoveById=200005,\
  ctl:ruleRemoveById=920540,\
  ctl:ruleRemoveById=942100,\
  ctl:ruleRemoveById=941100,\
  ctl:ruleRemoveById=930100,\
  ctl:ruleRemoveById=930130,\
  ctl:ruleRemoveById=932250"

# Allow wp-admin (editor, dashboard, media, etc.)
SecRule REQUEST_URI "@beginsWith /wp-admin/" "id:1001002,phase:1,pass,nolog,noauditlog,ctl:auditEngine=Off,\
  msg:'WordPress REST API Exception',\
  ctl:ruleRemoveById=941180,\
  ctl:ruleRemoveById=920450,\
  ctl:ruleRemoveById=949110,\
  ctl:ruleRemoveById=920100,\
  ctl:ruleRemoveById=200005,\
  ctl:ruleRemoveById=920540,\
  ctl:ruleRemoveById=942100,\
  ctl:ruleRemoveById=941100,\
  ctl:ruleRemoveById=930100,\
  ctl:ruleRemoveById=930130,\
  ctl:ruleRemoveById=932250"

# Allow HTML comments used by Gutenberg (e.g., <!-- wp:paragraph -->)
SecRule REQUEST_BODY "@rx <!--\s?wp:" "id:1001003,phase:2,pass,nolog,noauditlog,ctl:auditEngine=Off,ctl:ruleRemoveById=941180"

# Optional: Additional CRS rules often triggered by WordPress activity (expand as needed)
SecRuleRemoveById 950901
SecRuleRemoveById 953100
SecRuleRemoveById 958030
SecRuleRemoveById 958057
SecRuleRemoveById 960015
SecRuleRemoveById 973300
SecRuleRemoveById 973333
SecRuleRemoveById 973335
SecRuleRemoveById 973337
SecRuleRemoveById 973340
SecRuleRemoveById 973343
SecRuleRemoveById 981173
SecRuleRemoveById 981176
SecRuleRemoveById 981204
SecRuleRemoveById 981240
SecRuleRemoveById 981243
SecRuleRemoveById 981245
SecRuleRemoveById 981246
SecRuleRemoveById 981248
SecRuleRemoveById 981257

These exceptions only remove specific problematic rules for /wp-admin/ and /wp-json/ without disabling the entire WAF.

2. Include the File in NGINX

Open the server configuration file for your WordPress site (e.g., /etc/nginx/sites-enabled/example.com.conf) and add the ModSecurity block inside the HTTPS server block as shown:

example.com.conf excerpt
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/modsecurity.conf;
modsecurity_rules_file /etc/nginx/modsec/wp-admin-exceptions.conf;

3. Test Configuration and Reload NGINX

Use the following command to test your NGINX configuration and reload the web server if successful:

Bash
sudo nginx -t && sudo systemctl reload nginx

Result

With this configuration, ModSecurity reliably protects your WordPress site using the OWASP Core Rule Set — without blocking the admin area or the REST API. At the same time, it avoids disabling entire rule groups and preserves full control over critical security checks.

Comments

Leave a Reply