Enabling ModSecurity adds a web application firewall layer to Apache that inspects HTTP traffic and detects common exploit patterns before requests reach the application.
On Ubuntu and other Debian-style systems, ModSecurity is loaded as the security2_module and reads configuration and rule files via /etc/apache2/mods-available/security2.conf. The default module configuration typically pulls in any /etc/modsecurity/*.conf files, and the OWASP ModSecurity Core Rule Set (CRS) can be loaded from /usr/share/modsecurity-crs to provide a baseline ruleset.
Blocking mode can produce false positives and break logins, uploads, or APIs until exclusions are tuned. Starting in DetectionOnly mode keeps ModSecurity active while logging matches, which makes it possible to tune rules before enforcement; switching to On should be treated like a production change with monitoring and a quick rollback plan.
Related: How to enable or disable Apache modules
Related: How to test your Apache configuration
Steps to enable ModSecurity in Apache:
- Install the ModSecurity Apache module package.
$ sudo apt update Hit:1 http://ports.ubuntu.com/ubuntu-ports noble InRelease Get:2 http://ports.ubuntu.com/ubuntu-ports noble-updates InRelease [126 kB] Hit:3 http://ports.ubuntu.com/ubuntu-ports noble-backports InRelease Hit:4 http://ports.ubuntu.com/ubuntu-ports noble-security InRelease Get:5 http://ports.ubuntu.com/ubuntu-ports noble-updates/main arm64 Packages [1,781 kB] Get:6 http://ports.ubuntu.com/ubuntu-ports noble-updates/universe arm64 Packages [1,467 kB] Fetched 3,375 kB in 4s (834 kB/s) Reading package lists... Building dependency tree... Reading state information... 11 packages can be upgraded. Run 'apt list --upgradable' to see them. $ sudo apt install --assume-yes libapache2-mod-security2 Reading package lists... Building dependency tree... Reading state information... The following additional packages will be installed: liblua5.1-0 libyajl2 modsecurity-crs ##### snipped ##### Setting up libapache2-mod-security2 (2.9.7-1build3) ... apache2_invoke: Enable module security2 ##### snipped #####
- Install the OWASP ModSecurity Core Rule Set package.
$ sudo apt install --assume-yes modsecurity-crs Reading package lists... Building dependency tree... Reading state information... modsecurity-crs is already the newest version (3.3.5-2). modsecurity-crs set to manually installed. 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
- Enable the security2 module.
$ sudo a2enmod security2 Considering dependency unique_id for security2: Module unique_id already enabled Module security2 already enabled
- Enable the headers module.
$ sudo a2enmod headers Enabling module headers. To activate the new configuration, you need to run: systemctl restart apache2Many CRS deployments enable headers for rules that add or inspect HTTP headers.
- Create /etc/modsecurity/modsecurity.conf from the recommended template.
$ sudo cp --update=none /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
Files in /etc/modsecurity need a .conf suffix to be picked up by the default IncludeOptional /etc/modsecurity/*.conf pattern.
- Set SecRuleEngine to DetectionOnly for the initial reload.
$ sudo sed --in-place 's/^SecRuleEngine .*/SecRuleEngine DetectionOnly/' /etc/modsecurity/modsecurity.conf
DetectionOnly logs rule matches without blocking the request.
- Confirm the active SecRuleEngine value.
$ sudo grep -n '^SecRuleEngine' /etc/modsecurity/modsecurity.conf 7:SecRuleEngine DetectionOnly
- Confirm the CRS loader include is present.
$ sudo grep -n 'modsecurity-crs' /etc/apache2/mods-enabled/security2.conf 12: IncludeOptional /usr/share/modsecurity-crs/*.load
If the line is missing, add IncludeOptional /usr/share/modsecurity-crs/*.load inside the security2_module block in /etc/apache2/mods-available/security2.conf.
- Create the CRS configuration directory.
$ sudo install --directory --mode=0755 /etc/modsecurity/crs
- Ensure the CRS setup file exists.
$ sudo ls -l /etc/modsecurity/crs/crs-setup.conf -rw-r--r-- 1 root root 35016 Oct 2 2023 /etc/modsecurity/crs/crs-setup.conf
Keep CRS defaults during tuning; lower anomaly thresholds gradually once false positives are under control.
- Test the Apache configuration for syntax errors.
$ sudo apache2ctl -t
- Restart Apache to load ModSecurity.
$ sudo systemctl restart apache2
- Confirm security2_module is loaded.
$ sudo apache2ctl -M | grep security2 security2_module (shared)
- Create a local test rule in /etc/modsecurity/local-test.conf.
$ sudo tee /etc/modsecurity/local-test.conf >/dev/null <<'EOF' SecRule ARGS:modsectest "@streq 1" "id:1000000,phase:2,deny,status:403,log,msg:'ModSecurity local test rule hit'" EOF
Rule IDs must be unique; duplicate IDs can prevent Apache from reloading.
- Restart Apache to load the new test rule.
$ sudo systemctl restart apache2
- Record the audit log path from the SecAuditLog directive.
$ sudo grep -n '^SecAuditLog ' /etc/modsecurity/modsecurity.conf 205:SecAuditLog /var/log/apache2/modsec_audit.log
- Send a request that triggers the local test rule.
$ curl --silent --show-error -i 'http://127.0.0.1/?modsectest=1' HTTP/1.1 200 OK Date: Sat, 10 Jan 2026 21:28:05 GMT Server: Apache/2.4.58 (Ubuntu) Last-Modified: Sat, 10 Jan 2026 21:15:28 GMT ETag: "29af-6480f24215cc8" Accept-Ranges: bytes Content-Length: 10671 Vary: Accept-Encoding Content-Type: text/html ##### snipped #####
DetectionOnly mode should log the match while allowing the request to complete.
- Confirm the test rule message appears in the audit log.
$ sudo grep -n "ModSecurity local test rule hit" /var/log/apache2/modsec_audit.log | tail --lines=1 386:Apache-Error: [file "apache2_util.c"] [line 275] [level 3] [client 127.0.0.1] ModSecurity: Warning. String match "1" at ARGS:modsectest. [file "/etc/modsecurity/local-test.conf"] [line "1"] [id "1000000"] [msg "ModSecurity local test rule hit"] [hostname "127.0.0.1"] [uri "/"] [unique_id "aWLEZfMECnPgWKJR8VR0DAAAAEQ"]
Replace the path if SecAuditLog points to a different file.
- Switch SecRuleEngine to On to begin blocking.
$ sudo sed --in-place 's/^SecRuleEngine .*/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf
Enforcement can block legitimate traffic until exclusions are tuned; keep a rollback path available.
- Test the Apache configuration for syntax errors.
$ sudo apache2ctl -t
- Restart Apache to apply enforcement.
$ sudo systemctl restart apache2
- Verify the local test request returns HTTP 403.
$ curl --silent --show-error -i 'http://127.0.0.1/?modsectest=1' HTTP/1.1 403 Forbidden Date: Sat, 10 Jan 2026 21:28:05 GMT Server: Apache/2.4.58 (Ubuntu) Content-Length: 274 Content-Type: text/html; charset=iso-8859-1 ##### snipped #####
- Remove the local test rule file.
$ sudo rm --force /etc/modsecurity/local-test.conf
Restarting or reloading Apache removes the test rule from the active rule set.
- Reload Apache configuration.
$ sudo systemctl reload apache2
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
