How to Set Up mod_security for Apache

How to set up mod_security for Apache

I still remember the first time I watched mod_security‘s logs in real time on a client’s WordPress site — the sheer volume of automated SQL injection attempts, XSS probes, and vulnerability scanners hitting that server every single hour was honestly eye-opening. Most of that traffic never reaches your application logs because it gets blocked before it ever gets that far, which is exactly the point. mod_security is the closest thing Apache has to a proper web application firewall, and I consider it close to non-negotiable for any public-facing site handling user input.

Here’s how I set it up.

What Is mod_security

mod_security is an open-source Web Application Firewall (WAF) module for Apache (also available for Nginx and IIS). It inspects incoming HTTP requests — headers, body, query strings, cookies — against a set of rules, and blocks or logs requests that match known attack patterns: SQL injection, cross-site scripting, remote file inclusion, protocol violations, and more.

Most people pair it with the OWASP Core Rule Set (CRS), a community-maintained, regularly updated set of generic attack-detection rules. Writing your own rules from scratch is possible, but almost nobody does that as a starting point.

Prerequisites

  • Apache installed and running
  • Root or sudo access
  • A staging environment — I never enable a new WAF ruleset directly in blocking mode on production first
  • Basic familiarity with reading logs, since initial tuning requires reviewing false positives

Step 1: Install mod_security

Debian/Ubuntu

sudo apt update
sudo apt install libapache2-mod-security2

CentOS/RHEL

sudo dnf install mod_security

Enable it:

sudo a2enmod security2       # Debian/Ubuntu
sudo systemctl restart apache2

On CentOS/RHEL, the module typically loads automatically once installed; confirm with:

apachectl -M | grep security

Step 2: Enable the Default Configuration

Debian/Ubuntu ships a recommended config file that needs renaming to take effect:

cd /etc/modsecurity
sudo cp modsecurity.conf-recommended modsecurity.conf

Open modsecurity.conf and find the SecRuleEngine directive. This is the single most important setting in the whole file:

SecRuleEngine DetectionOnly

I always start in DetectionOnly mode. This logs what would have been blocked without actually blocking anything — critical for catching false positives before they start breaking legitimate traffic on a live site.

Step 3: Install the OWASP Core Rule Set

The bundled default rules are minimal. For real protection, install the OWASP CRS:

cd /etc/modsecurity
sudo git clone https://github.com/coreruleset/coreruleset.git owasp-crs
cd owasp-crs
sudo cp crs-setup.conf.example crs-setup.conf

Then include it in your Apache config (typically in /etc/apache2/mods-enabled/security2.conf on Debian/Ubuntu):

<IfModule security2_module>
    SecDataDir /var/cache/modsecurity
    IncludeOptional /etc/modsecurity/*.conf
    IncludeOptional /etc/modsecurity/owasp-crs/crs-setup.conf
    IncludeOptional /etc/modsecurity/owasp-crs/rules/*.conf
</IfModule>

Step 4: Test the Configuration

sudo apachectl configtest
sudo systemctl restart apache2   # Debian/Ubuntu
sudo systemctl restart httpd     # CentOS/RHEL

Step 5: Monitor in Detection Mode

With SecRuleEngine DetectionOnly active, browse and use your site normally for a few days to a week, while watching the audit log:

sudo tail -f /var/log/apache2/modsec_audit.log

Or, if using the Apache error log for alerts:

sudo tail -f /var/log/apache2/error.log | grep -i modsecurity

I pay close attention to any legitimate actions (form submissions, admin panel usage, file uploads) that get flagged — these are false positives I need to tune out before going live in blocking mode.

Step 6: Tune Out False Positives

When I find a false positive, I don’t disable the whole rule globally if I can avoid it — I scope the exception as narrowly as possible. Each blocked request in the audit log includes a rule ID. For example, if rule 941100 is falsely triggering on a legitimate admin form field:

<LocationMatch "/wp-admin/post.php">
    SecRuleRemoveById 941100
</LocationMatch>

Scoping exceptions to specific paths (rather than disabling rules site-wide) keeps your overall protection much stronger.

Step 7: Switch to Blocking Mode

Once I’m confident false positives are handled, I flip the switch:

SecRuleEngine On

Test and reload:

sudo apachectl configtest
sudo systemctl reload apache2

From this point on, matching requests are actually blocked (typically with a 403 response) rather than just logged.

Setting the Paranoia Level

The OWASP CRS has a “paranoia level” (1–4) controlling how aggressive detection is. Higher levels catch more attacks but also generate more false positives. In crs-setup.conf:

SecAction \
    "id:900000,\
    phase:1,\
    nolog,\
    pass,\
    t:none,\
    setvar:tx.paranoia_level=1"

I stay at paranoia level 1 for most general-purpose sites — it’s the level the CRS project itself recommends as a safe default. I only go higher for genuinely high-security applications (banking, healthcare) where I have time budgeted for the extra tuning work that comes with it.

Real-World Use Cases

  • WordPress and other CMS platforms: Constant targets for automated exploit scanners; mod_security blocks a large share of this traffic before it reaches PHP at all.
  • Login and form endpoints: Protects against SQL injection and XSS attempts in user-submitted data.
  • API endpoints: Detects malformed or malicious payloads targeting your backend.
  • Compliance requirements: Some PCI-DSS assessments specifically call for a WAF on any server processing payment data.

Troubleshooting Tips

  • Legitimate requests getting blocked (403 errors): Check the audit log for the specific rule ID and scope an exception rather than disabling protection broadly.
  • Performance concerns on high-traffic sites: Review paranoia level and rule count; the CRS project provides guidance on performance-tuning rule exclusions for busy sites.
  • Audit log growing very large: Adjust SecAuditLogParts to log only what you actually need, and set up log rotation specifically for the ModSecurity audit log.
  • Rules not applying at all: Confirm IncludeOptional paths actually point to where you cloned the CRS, and check apachectl -M | grep security to confirm the module itself loaded.

Common Mistakes to Avoid

  1. Enabling blocking mode immediately without a detection-only tuning period — this is the single most common way people break their own sites with mod_security.
  2. Globally disabling rules instead of scoping exceptions to specific paths or parameters.
  3. Never updating the CRS, missing detection improvements for newer attack patterns.
  4. Ignoring the audit log entirely once it’s set up — a WAF you never review is only half doing its job.
  5. Setting paranoia level too high too fast, generating an overwhelming number of false positives that lead to frustration and premature abandonment of the tool.

Security Best Practices

  • Start in DetectionOnly, tune carefully, then switch to blocking mode.
  • Keep the OWASP CRS updated regularly — attack patterns evolve constantly.
  • Scope rule exceptions as narrowly as possible (specific paths/parameters, not global disables).
  • Combine mod_security with other layers: rate limiting, fail2ban, proper input validation in your application code. A WAF is a layer, not a replacement for secure coding practices.
  • Regularly review audit logs for patterns that might indicate targeted, persistent attacks worth escalating.

Performance Optimization

  • Only enable the rule categories you actually need — the CRS lets you selectively include/exclude rule files.
  • Use SecAuditLogParts to limit what gets logged, reducing I/O overhead on high-traffic sites.
  • Consider caching/exempting static asset requests from full rule processing, since they rarely carry attack payloads.
  • Benchmark request latency before and after enabling mod_security under realistic load to catch any unexpected performance regressions early.

Frequently Asked Questions

Q: Is mod_security free? A: Yes, mod_security itself and the OWASP Core Rule Set are both free and open source.

Q: Will mod_security slow down my site? A: There’s a small amount of overhead per request, but for most sites it’s negligible relative to the security benefit. High-traffic sites should benchmark and tune rule sets accordingly.

Q: What’s the difference between mod_security and a firewall like ufw or iptables? A: ufw/iptables operate at the network level (IPs, ports); mod_security operates at the HTTP application layer, inspecting actual request content for attack patterns.

Q: How do I know if mod_security is blocking a legitimate request? A: Check the audit log for a 403 response and the specific rule ID that triggered — that’s your starting point for tuning an exception.

Q: Should I run mod_security in DetectionOnly mode permanently? A: No — detection-only mode logs threats but doesn’t stop them. It’s meant as a temporary tuning phase before switching to full blocking mode.

Summary and Key Takeaways

mod_security is one of the highest-value security additions you can make to an Apache server, but it needs to be rolled out carefully. Here’s what to remember:

  • Install mod_security and pair it with the OWASP Core Rule Set for meaningful protection out of the box.
  • Always start in DetectionOnly mode and tune out false positives before switching to blocking.
  • Scope rule exceptions narrowly instead of disabling protection broadly.
  • Keep the rule set updated and review audit logs periodically.
  • Treat it as one layer of a broader security strategy, not a silver bullet on its own.

Done right, it quietly blocks an enormous amount of automated attack traffic without you ever having to think about it day to day.

References

Total
1
Shares

Leave a Reply

Previous Post
How to use mod_proxy for load balancing with Apache

How to Use mod_proxy for Load Balancing with Apache

Next Post
How to enable mod_expires for caching in Apache

How to Enable mod_expires for Caching in Apache

Related Posts