How to Install ModSecurity and OWASP CRS with Apache on Debian 13, 12 and 11

Install ModSecurity with OWASP CRS on Debian 13, 12 and 11. Covers repository setup, Apache configuration, CRS rule tuning.

Last updatedAuthorJoshua JamesRead time12 minGuide typeDebian

A web application firewall becomes useful when an Apache site needs more than TLS and normal request logging. ModSecurity with Apache on Debian inspects HTTP requests before they reach applications such as WordPress, phpMyAdmin, Nextcloud, or custom PHP code, then uses the OWASP Core Rule Set (CRS) to detect common SQL injection, cross-site scripting, file traversal, and command-injection patterns.

Debian archive packages are the default path for most systems because they work across Debian’s supported architectures, including arm64. Digitalwave is an optional third-party source for amd64 and i386 systems that specifically need newer backports. CRS can stay package-managed or be replaced with a signed CRS 4 release from the official OWASP CRS GitHub releases. Start in monitoring mode on production sites, review the audit log, then enable blocking after you understand which requests your application normally sends.

Choose a ModSecurity Method on Debian

Debian provides ModSecurity and CRS packages in its default repositories for Debian 13 (Trixie), Debian 12 (Bookworm), and Debian 11 (Bullseye). Use those packages unless you have a clear reason to add a third-party source. The Digitalwave ModSecurity repository publishes newer Debian backports, but its Debian package indexes are limited to amd64 and i386.

PathWhat It InstallsArchitecture FitUpdate OwnerBest For
Debian archive packagesDebian archive versions of libapache2-mod-security2 and modsecurity-crsDefault choice for Debian-supported architectures, including arm64APTMost servers, ARM systems, and deployments that prefer distro-maintained packages
Optional Digitalwave packagesDigitalwave candidates for libapache2-mod-security2, its ModSecurity library, and packaged modsecurity-crsamd64 and i386 from DigitalwaveAPTServers that specifically need newer Digitalwave backports and accept a third-party repository
APT-managed module plus signed CRS 4 releaseApache module from the selected APT source with CRS files installed under /etc/apache2/modsecModule follows the selected package source; CRS tarball is architecture-independentAPT for the module, helper script for CRSAdministrators who specifically want the latest CRS 4 release and are willing to maintain manual rule-set updates

The package names matter. libapache2-mod-security2 is the Apache module package that loads ModSecurity 2 for Apache. modsecurity-crs is the packaged OWASP Core Rule Set. The package-managed CRS files are loaded through /usr/share/modsecurity-crs/owasp-crs.load on Debian after the package is installed.

Use one CRS source at a time. Do not leave both packaged CRS includes and a manual CRS 4 include active in /etc/apache2/mods-available/security2.conf, because duplicate rules can break Apache startup with repeated rule IDs.

Install ModSecurity with Apache on Debian

The installation starts with Apache and small helper tools, then installs the Debian archive packages. Add Digitalwave only when newer backports are worth the extra repository, architecture limit, and source cleanup.

Update Debian and Install Prerequisites

Refresh APT metadata first so dependency resolution starts from the current enabled Debian sources.

sudo apt update

Install Apache and the small tools used by the optional Digitalwave and CRS 4 sections later.

sudo apt install apache2 ca-certificates curl gpg jq

If Apache is already installed, APT keeps the existing package and installs only missing prerequisites. These commands use sudo because package installation, repository files, and Apache configuration live in root-owned system paths. For a broader web-server baseline before adding a WAF, use the Apache installation guide for Debian.

Install Debian Archive Packages

Install the Apache module and packaged CRS from Debian’s own repositories. No third-party repository is required for this baseline path.

sudo apt install libapache2-mod-security2 modsecurity-crs

Check Digitalwave Architecture Support

Use this optional check only if you plan to add Digitalwave for newer backports. Digitalwave’s Debian package indexes are limited to amd64 and i386. If the command prints arm64, stay with the Debian archive package path.

dpkg --print-architecture
amd64

Add the Optional Digitalwave Repository

On amd64 or i386, add Digitalwave only when you want its newer ModSecurity backports. Download the repository key and store it as a dedicated keyring. The --yes option prevents gpg from prompting if you rerun the command during troubleshooting.

curl -fsSL https://modsecurity.digitalwave.hu/archive.key | sudo gpg --dearmor --yes -o /usr/share/keyrings/digitalwave-modsecurity.gpg

Create the APT source with an architecture guard. Debian 13, 12, and 11 use different codenames, so the source setup reads /etc/os-release instead of relying on lsb_release, which is often missing on minimal Debian systems.

. /etc/os-release
arch=$(dpkg --print-architecture)

case "$arch" in
    amd64|i386)
        printf '%s\n' \
        'Types: deb' \
        'URIs: http://modsecurity.digitalwave.hu/debian/' \
        "Suites: ${VERSION_CODENAME}-backports" \
        'Components: main' \
        "Architectures: ${arch}" \
        'Signed-By: /usr/share/keyrings/digitalwave-modsecurity.gpg' | sudo tee /etc/apt/sources.list.d/digitalwave-modsecurity.sources > /dev/null
        ;;
    *)
        printf 'Digitalwave ModSecurity packages are not published for %s. Use the Debian archive package path instead.\n' "$arch" >&2
        false
        ;;
esac

Add an APT preference so the ModSecurity package family comes from Digitalwave when both Debian and Digitalwave provide a candidate. Debian 13 uses the libmodsecurity3t64 library name, while Debian 12 and 11 use libmodsecurity3, so the pin includes both names.

printf '%s\n' \
'Package: libapache2-mod-security2 libmodsecurity3 libmodsecurity3t64 modsecurity-crs' \
'Pin: origin modsecurity.digitalwave.hu' \
'Pin-Priority: 900' | sudo tee /etc/apt/preferences.d/99modsecurity > /dev/null

Refresh APT again so the new source is available.

sudo apt update

Verify the Optional Digitalwave Candidate

Before using the optional Digitalwave packages, check that APT sees Digitalwave as the selected source for both package names.

apt-cache policy libapache2-mod-security2 modsecurity-crs

On a correctly configured Debian 13 system, the important details are the Digitalwave URL, the trixie-backports suite, and priority 900. Debian 12 shows bookworm-backports, and Debian 11 shows bullseye-backports.

libapache2-mod-security2:
  Candidate: 2.9.14-1~pre1+0~20260428~bpo13+02eed22
  Version table:
     2.9.14-1~pre1+0~20260428~bpo13+02eed22 900
        500 http://modsecurity.digitalwave.hu/debian trixie-backports/main amd64 Packages

modsecurity-crs:
  Candidate: 3.3.9-1~bpo13+1
  Version table:
     3.3.9-1~bpo13+1 900
        500 http://modsecurity.digitalwave.hu/debian trixie-backports/main amd64 Packages

If APT still prefers Debian’s default package on amd64 or i386, recheck the source file name, the release codename, and the preference file before installing. On arm64, the expected path is the Debian archive package because Digitalwave does not publish a Debian binary-arm64 package index.

Install or Upgrade the Apache Module and Packaged CRS

If you stayed with Debian archive packages, this command is already complete. If you added Digitalwave, rerun it so APT installs the Digitalwave candidates. The module package enables the Apache security2 module, and the CRS package installs the rule-set loader under /usr/share/modsecurity-crs.

sudo apt install libapache2-mod-security2 modsecurity-crs

Confirm that Apache can see the module.

sudo apachectl -M 2>/dev/null | grep security2
 security2_module (shared)

Check the packaged CRS loader path as well. This path is useful when troubleshooting include problems or answering the common Debian package-path question.

find /usr/share/modsecurity-crs -maxdepth 2 -type f \( -name '*.load' -o -name 'crs-setup.conf' \)
/usr/share/modsecurity-crs/owasp-crs.load

Configure ModSecurity on Apache

ModSecurity installs a recommended configuration template but does not use it until you copy it into place. The active file is /etc/modsecurity/modsecurity.conf, and Apache loads it through /etc/apache2/mods-available/security2.conf.

Activate the Recommended Configuration

Copy the recommended configuration to the active configuration path.

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

Set the rule engine. For a new live site, use DetectionOnly first so requests are logged but not blocked while you tune false positives. For a lab server or a site ready for active blocking, use On.

sudo sed -i 's/^SecRuleEngine .*/SecRuleEngine DetectionOnly/' /etc/modsecurity/modsecurity.conf

Switch to blocking mode when you are ready to reject matching requests.

sudo sed -i 's/^SecRuleEngine .*/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf

Production sites should spend time in DetectionOnly before active blocking. A WAF can block legitimate login forms, search boxes, API requests, or admin actions until the CRS exclusions match the application.

Tune Audit Log Detail

The audit log is the fastest way to understand why ModSecurity acted on a request. The following setting keeps useful request, response, and matched-rule sections in the log.

sudo sed -i 's/^SecAuditLogParts .*/SecAuditLogParts ABCEFHJKZ/' /etc/modsecurity/modsecurity.conf

The letters represent log parts. The most useful pieces for first tuning are request headers, request body, response status, matched rule data, and the audit trailer. If response-body inspection creates noise on your site, tune it deliberately instead of disabling the whole rule engine.

Test and Restart Apache

Run an Apache syntax test before restarting. A server-name warning such as AH00558 is common on fresh Apache installations; Syntax OK is the success signal for this step.

sudo apachectl configtest
Syntax OK

Restart Apache so the module, base configuration, and CRS includes load together.

sudo systemctl restart apache2

Verify ModSecurity Blocks a Test Request

Use a local request that should trigger CRS command-injection detection. This request targets 127.0.0.1, so it does not depend on DNS, TLS, a virtual host, or an open firewall port.

curl -sS -o /dev/null -w '%{http_code}\n' 'http://127.0.0.1/?exec=/bin/bash'

When SecRuleEngine is set to On and CRS is loaded, Apache should return 403.

403

If you intentionally left the engine in DetectionOnly, the same request may return 200 or another normal application response while still writing a ModSecurity audit entry. That is expected because monitoring mode logs matches without blocking them.

Inspect the audit log after the test. The tail command examples can help if you want more log-following patterns.

sudo tail -n 40 /var/log/apache2/modsec_audit.log

Look for the request URI, the returned status, and a matched rule ID such as [id "932100"] or another CRS rule number. The exact rule can change as CRS updates, so use the ID in your own log when building an exclusion.

Install the Latest Signed OWASP CRS 4 Release

The package-managed method is easier to maintain, but some administrators want the latest CRS 4 release from the official project. The OWASP CRS installation documentation recommends using supported releases and verifying release signatures with the CRS project key. This method downloads from the official CRS GitHub releases, keeps the Apache module under APT, and installs CRS files under /etc/apache2/modsec.

This method replaces the CRS include path used by Apache. Use it only if you are comfortable maintaining manual CRS updates and keeping backups of local exclusions.

Download and Verify the Latest CRS Release

Resolve the latest release tag from GitHub, download the signed minimal release tarball and matching signature, then verify the signature with the CRS project key in a temporary GPG home. The command resolves the release at runtime instead of hard-coding a CRS version into the install steps.

prepare_crs_download() {
    workdir=$(mktemp -d)
    crs_tag=$(curl -fsSL https://api.github.com/repos/coreruleset/coreruleset/releases/latest | jq -r '.tag_name')
    case "$crs_tag" in
        v*) ;;
        *) echo "Could not resolve the latest CRS tag." >&2; rm -rf "$workdir"; return 1 ;;
    esac
    crs_version=${crs_tag#v}
    archive="$workdir/coreruleset-${crs_version}-minimal.tar.gz"
    signature="$workdir/coreruleset-${crs_version}-minimal.tar.gz.asc"
    release_url="https://github.com/coreruleset/coreruleset/releases/download/${crs_tag}"

    curl -fsSLo "$archive" "${release_url}/coreruleset-${crs_version}-minimal.tar.gz"
    curl -fsSLo "$signature" "${release_url}/coreruleset-${crs_version}-minimal.tar.gz.asc"

    GNUPGHOME="$workdir/gnupg"
    export GNUPGHOME
    mkdir -m 700 "$GNUPGHOME"
    gpg --fetch-key https://coreruleset.org/security.asc
    gpg --verify "$signature" "$archive"
}
prepare_crs_download

A successful verification includes a good signature from the OWASP Core Rule Set key and the project fingerprint.

gpg: Good signature from "OWASP Core Rule Set <security@coreruleset.org>" [unknown]
Primary key fingerprint: 3600 6F0E 0BA1 6783 2158  8211 38EE ACA1 AB8A 6E72

Install CRS 4 Files Under Apache

Create the CRS directory, extract the verified archive, and activate the example CRS setup file. Keep these commands in the same terminal session so archive, crs_tag, and crs_version still exist.

Replacing the manual CRS tree deletes existing files under /etc/apache2/modsec. On systems with prior CRS 4 tuning, copy custom crs-setup.conf, exclusion files, or plugins before recreating the directory.

sudo rm -rf /etc/apache2/modsec
sudo mkdir -p /etc/apache2/modsec
sudo tar xzf "$archive" -C /etc/apache2/modsec --strip-components=1
sudo cp /etc/apache2/modsec/crs-setup.conf.example /etc/apache2/modsec/crs-setup.conf
sudo cp /etc/apache2/modsec/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example /etc/apache2/modsec/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
sudo cp /etc/apache2/modsec/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example /etc/apache2/modsec/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf

Replace the ModSecurity Apache module include file so Apache loads the manual CRS 4 path. The plugin includes are optional, but keeping them in the file makes future CRS plugins easier to enable.

sudo tee /etc/apache2/mods-available/security2.conf > /dev/null <<'EOF'
<IfModule security2_module>
    SecDataDir /var/cache/modsecurity
    IncludeOptional /etc/modsecurity/*.conf
    IncludeOptional /etc/apache2/modsec/crs-setup.conf
    IncludeOptional /etc/apache2/modsec/plugins/*-config.conf
    IncludeOptional /etc/apache2/modsec/plugins/*-before.conf
    IncludeOptional /etc/apache2/modsec/rules/*.conf
    IncludeOptional /etc/apache2/modsec/plugins/*-after.conf
</IfModule>
EOF

Test Apache and restart it.

sudo apachectl configtest
sudo systemctl restart apache2

Run the same local malicious-request check. A 403 response confirms that the manual CRS 4 include path is active in blocking mode.

curl -sS -o /dev/null -w '%{http_code}\n' 'http://127.0.0.1/?exec=/bin/bash'
403

Remove the temporary download directory after the signature check and extraction are complete.

rm -rf "$workdir"
unset GNUPGHOME

Get Started with ModSecurity Tuning

A fresh WAF install needs a tuning phase. CRS is intentionally generic, so it catches a broad range of attack patterns, but real applications can send unusual payloads that look suspicious. Tune from logs and specific rule IDs instead of disabling large rule groups too early.

Use DetectionOnly for First Observation

Use monitoring mode while you browse the application, submit forms, sign in to admin pages, upload files, and run API requests that normal users depend on.

sudo sed -i 's/^SecRuleEngine .*/SecRuleEngine DetectionOnly/' /etc/modsecurity/modsecurity.conf
sudo systemctl reload apache2

After reviewing the audit log and adding targeted exclusions, switch to blocking mode.

sudo sed -i 's/^SecRuleEngine .*/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf
sudo systemctl reload apache2

Understand Paranoia Levels

CRS uses paranoia levels to decide how aggressively rules inspect requests. Level 1 is the default and the right starting point for most public sites. Higher levels can catch more suspicious patterns, but they also increase false positives and require more exclusions.

LevelBehaviorUse It When
PL1Default CRS coverage with the lowest normal false-positive rateYou are protecting a typical public website or application
PL2More rules and stricter request inspectionYou can review logs regularly and tune application-specific issues
PL3Strict detection that often needs careful exclusionsThe application is high risk and you have a tuning process
PL4Maximum CRS strictnessSpecialized environments where false positives are acceptable or heavily managed

For CRS 4 installed under /etc/apache2/modsec, edit /etc/apache2/modsec/crs-setup.conf. For the packaged CRS path, inspect the packaged setup file and loader under /usr/share/modsecurity-crs. Keep local customizations documented because package updates and manual CRS upgrades can replace example files.

Enable Application-Specific Exclusions

CRS ships application exclusion packages for common software. If you run WordPress, phpMyAdmin, Drupal, Nextcloud, or another supported application, enable the relevant exclusions in crs-setup.conf instead of writing broad custom exclusions first. If the protected site is a WordPress stack, the WordPress with Apache on Debian guide is a useful companion for the application layer.

For CRS 4, check the current crs-setup.conf.example comments and plugin documentation before enabling older CRS 3 exclusion patterns. CRS 4 changed parts of the exclusion package workflow, so use the comments in the installed release rather than copying a stale rule from an older CRS 3 tutorial.

Create a Targeted Rule Exclusion

Use the before-CRS exclusion file for exceptions that must apply before normal CRS rules run. For the manual CRS 4 path, open this file:

sudo nano /etc/apache2/modsec/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf

For the packaged CRS path, locate the active exclusion file from the package loader before editing:

grep -R "EXCLUSION-RULES" /usr/share/modsecurity-crs /etc/modsecurity 2>/dev/null

A narrow path-based exclusion is safer than disabling a rule globally. This example disables SQL injection and XSS rule ranges only for a known internal API path.

SecRule REQUEST_URI "@beginsWith /admin/api/" "id:1000,phase:1,pass,nolog,ctl:ruleRemoveById=941000-942999"

Reload Apache after exclusions and repeat the request that was previously blocked.

sudo apachectl configtest
sudo systemctl reload apache2

Connect ModSecurity with Broader Apache Security

ModSecurity inspects HTTP content, but it does not replace TLS, application updates, least-privilege permissions, or abuse controls. After the WAF is working, secure public sites with Let’s Encrypt for Apache on Debian and consider Fail2Ban on Debian for repeated authentication or abuse patterns that are better handled by IP-based jails.

Update ModSecurity and OWASP CRS

Update Package-Managed ModSecurity and CRS

When you use either package-managed path, normal APT updates handle the Apache module and packaged CRS. Digitalwave users receive the Digitalwave candidate on supported architectures; Debian archive users receive Debian’s candidate. To update only this package family, use --only-upgrade.

sudo apt update
sudo apt install --only-upgrade libapache2-mod-security2 modsecurity-crs

Restart Apache after a ModSecurity or CRS package update so the running workers use the refreshed module and rules.

sudo apachectl configtest
sudo systemctl restart apache2

Create a CRS 4 Update Helper

If you installed CRS 4 manually, create a helper that downloads the latest signed CRS release, backs up the current CRS directory, preserves your active crs-setup.conf and standard exclusion files, tests Apache, and rolls back if the syntax test fails.

sudo tee /usr/local/sbin/update-owasp-crs > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

CRS_DIR="/etc/apache2/modsec"
workdir=$(mktemp -d)
cleanup() {
    rm -rf "$workdir"
}
trap cleanup EXIT

latest_json=$(curl -fsSL https://api.github.com/repos/coreruleset/coreruleset/releases/latest)
crs_tag=$(printf '%s\n' "$latest_json" | jq -r '.tag_name')
case "$crs_tag" in
v*) ;;
*)
    echo "Could not resolve the latest CRS tag." >&2
    exit 1
    ;;
esac
crs_version=${crs_tag#v}
release_url="https://github.com/coreruleset/coreruleset/releases/download/${crs_tag}"

archive="$workdir/coreruleset-${crs_version}-minimal.tar.gz"
signature="$workdir/coreruleset-${crs_version}-minimal.tar.gz.asc"
curl -fsSLo "$archive" "${release_url}/coreruleset-${crs_version}-minimal.tar.gz"
curl -fsSLo "$signature" "${release_url}/coreruleset-${crs_version}-minimal.tar.gz.asc"

GNUPGHOME="$workdir/gnupg"
export GNUPGHOME
mkdir -m 700 "$GNUPGHOME"
gpg --fetch-key https://coreruleset.org/security.asc >/dev/null 2>&1
gpg --verify "$signature" "$archive"

backup="/etc/apache2/modsec-backup-${crs_version}-$(date +%Y%m%d%H%M%S)"
backup_created=0
if [ -d "$CRS_DIR" ]; then
    sudo cp -a "$CRS_DIR" "$backup"
    backup_created=1
fi

sudo mkdir -p "$CRS_DIR"
sudo rm -rf "${CRS_DIR:?}"/*
sudo tar xzf "$archive" -C "$CRS_DIR" --strip-components=1

if [ "$backup_created" -eq 1 ] && [ -f "$backup/crs-setup.conf" ]; then
    sudo cp "$backup/crs-setup.conf" "$CRS_DIR/crs-setup.conf"
else
    sudo cp "$CRS_DIR/crs-setup.conf.example" "$CRS_DIR/crs-setup.conf"
fi

for example in \
    REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example \
    RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example; do
    target="${example%.example}"
    if [ "$backup_created" -eq 1 ] && [ -f "$backup/rules/$target" ]; then
        sudo cp "$backup/rules/$target" "$CRS_DIR/rules/$target"
    else
        sudo cp "$CRS_DIR/rules/$example" "$CRS_DIR/rules/$target"
    fi
done

if sudo apachectl configtest; then
    sudo systemctl reload apache2
    echo "OWASP CRS $crs_tag installed and Apache reloaded."
else
    echo "Apache syntax check failed." >&2
    if [ "$backup_created" -eq 1 ]; then
        echo "Restoring $backup." >&2
        sudo rm -rf "${CRS_DIR:?}"/*
        sudo cp -a "$backup"/. "$CRS_DIR"/
        sudo apachectl configtest
    else
        echo "No previous CRS backup was available for automatic rollback." >&2
    fi
    exit 1
fi
EOF
sudo chmod +x /usr/local/sbin/update-owasp-crs

Run the helper manually after reviewing recent CRS release notes or when you are ready to test a rule update.

sudo /usr/local/sbin/update-owasp-crs

A successful run prints the signature check, Apache syntax result, and resolved CRS tag. The final line changes when GitHub publishes a newer CRS release.

gpg: Good signature from "OWASP Core Rule Set <security@coreruleset.org>" [unknown]
Syntax OK
OWASP CRS v4.26.0 installed and Apache reloaded.

The helper keeps backups under /etc/apache2/ with names that start with modsec-backup- and include the CRS version plus a timestamp. If you keep custom plugins or nonstandard rule files under the CRS directory, restore them from the backup after reviewing compatibility with the new release. Remove old backups only after the site has run normally with the new rules.

Troubleshoot ModSecurity on Debian

APT Reports a Digitalwave 404 Error

A 404 during sudo apt update usually means the source file points at the wrong repository path, suite, or architecture. Use the Digitalwave URI with /debian/ and the verified ${VERSION_CODENAME}-backports suite. If the error contains binary-arm64, remove the Digitalwave source and use the Debian archive package path.

dpkg --print-architecture
grep -R "modsecurity.digitalwave.hu" /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null

If you are on arm64, remove the Digitalwave source and install the Debian archive packages. If you are on amd64 or i386 and find an older file that lacks /debian/ after the host or lists both trixie and trixie-backports, remove or replace it with the guarded DEB822 source, then refresh APT.

sudo rm -f /etc/apt/sources.list.d/digitalwave-modsecurity.list
sudo rm -f /etc/apt/sources.list.d/digitalwave-modsecurity.sources
sudo apt update

Apache Fails After Editing security2.conf

Run the syntax test first and read the failing include path. Missing files normally mean the configured CRS path does not match the method you installed.

sudo apachectl configtest

For the package-managed CRS path, the default include should reference the packaged CRS loader under /usr/share/modsecurity-crs. For the manual CRS 4 path, security2.conf should reference /etc/apache2/modsec/crs-setup.conf and /etc/apache2/modsec/rules/*.conf.

The Test Request Returns 200 Instead of 403

A 200 response means the request was not blocked. That can be correct in DetectionOnly mode, but in blocking mode check the rule engine, module, CRS includes, and Apache reload state.

grep '^SecRuleEngine' /etc/modsecurity/modsecurity.conf
sudo apachectl -M 2>/dev/null | grep security2
sudo apachectl -t -D DUMP_INCLUDES | grep -E 'modsecurity|modsec|crs|security2'
sudo systemctl restart apache2

If SecRuleEngine is On, the module is loaded, and no CRS files appear in the include dump, fix /etc/apache2/mods-available/security2.conf for the method you chose.

Legitimate Requests Are Blocked

Do not disable ModSecurity globally as the first response. Find the rule ID, confirm the request path or parameter, and add the narrowest exclusion that lets the legitimate request through.

sudo grep -E 'id "|Access denied|ModSecurity' /var/log/apache2/modsec_audit.log | tail -n 40

When the site is busy, it can be easier to reproduce the blocked action once and then inspect the newest audit entries immediately. Use the rule ID from your own log entry, not an example ID copied from another site.

The Audit Log Grows Too Quickly

Busy sites can create large ModSecurity audit logs. Add a logrotate file if your Debian system does not already rotate the audit log for your chosen package layout.

sudo tee /etc/logrotate.d/modsecurity > /dev/null <<'EOF'
/var/log/apache2/modsec_audit.log {
    daily
    rotate 14
    missingok
    compress
    delaycompress
    notifempty
    create 640 root adm
}
EOF

Test the logrotate configuration without forcing an actual rotation.

sudo logrotate -d /etc/logrotate.d/modsecurity

Remove ModSecurity from Debian

Removal depends on whether you used only packaged CRS or also installed CRS 4 manually. Disable the Apache module first, then remove packages, manual CRS files, the update helper, and the Digitalwave APT source if it was used.

Disable the Apache Module

sudo a2dismod security2
sudo systemctl restart apache2

Remove Packages and Configuration Files

Purge the Apache module package and packaged CRS.

sudo apt remove --purge libapache2-mod-security2 modsecurity-crs

The next commands delete ModSecurity configuration, manual CRS files, CRS backups, the optional update helper, and any Digitalwave repository files. Back up custom exclusions before running them.

sudo rm -rf /etc/modsecurity
sudo rm -rf /etc/apache2/modsec
sudo rm -rf /etc/apache2/modsec-backup-*
sudo rm -f /etc/logrotate.d/modsecurity
sudo rm -f /usr/local/sbin/update-owasp-crs
sudo rm -f /etc/apt/sources.list.d/digitalwave-modsecurity.sources
sudo rm -f /etc/apt/sources.list.d/digitalwave-modsecurity.list
sudo rm -f /etc/apt/preferences.d/99modsecurity
sudo rm -f /usr/share/keyrings/digitalwave-modsecurity.gpg

Refresh APT after removing repository files so package policy no longer shows Digitalwave as a source.

sudo apt update
apt-cache policy libapache2-mod-security2 modsecurity-crs

Review unused dependencies before removing them. If the simulation lists only packages that were installed for this workflow, run the real autoremove command.

apt-get -s autoremove --purge
sudo apt autoremove --purge

Verify the Module Is Gone

if sudo apachectl -M 2>/dev/null | grep -q security2; then
    echo "ModSecurity is still loaded."
else
    echo "ModSecurity module is not loaded."
fi
ModSecurity module is not loaded.

Conclusion

Apache is running with ModSecurity and a verified OWASP CRS path on Debian, with package ownership, architecture-aware source choice, rule-engine mode, audit logs, updates, and removal covered. Keep the WAF in monitoring mode long enough to tune real application traffic, then pair active blocking with HTTPS, application updates, and focused log review.

Share this guide

Help another Linux user troubleshoot faster

Share this guide with someone troubleshooting Linux systems or saving it for later.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show our tutorials more often in Top Stories and mark them as preferred in AI Mode and AI Overviews when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee

4 thoughts on “How to Install ModSecurity and OWASP CRS with Apache on Debian 13, 12 and 11”

    • Thanks for the feedback, Rusty. You are right about the current Digitalwave repository path is architecture-limited. Digitalwave publishes Debian backports indexes for amd64 and i386, so an arm64 host can fail when the source file uses the local architecture. ModSecurity itself is not the issue, because Debian’s own libapache2-mod-security2 package is available for arm64. On arm64, use the Debian archive packages for now:

      sudo apt install libapache2-mod-security2 modsecurity-crs

      I have updated the article so Debian archive packages are the default path. Digitalwave is now treated as an optional newer-backports method for amd64/i386 only unless its repository adds arm64 packages.

      Reply
  1. Not a bad article, everything is explained in detail, but some parts feel overly drawn out. I liked that it covers not only the installation of ModSecurity but also the setup of the OWASP Core Rule Set. This is useful for those just starting to explore web security. The section on paranoia levels and handling false positives stood out—it clearly highlights what to focus on to ensure stable system performance. One thing I wish it had is more practical examples for setting up exclusions for popular applications like WordPress or phpMyAdmin. Overall, it’s a solid resource, especially for beginners.

    Reply
    • Thanks Alex, I could not cover too much more into OWASP core rule set as the needs a separate beginners guide and probably advanced guides, so many possibilities with the core rule set to customize, whitelist, create custom options and much more. The guide was just focused on installing it and doing a brief run through as you mentioned.

      Appreciate the feedback.

      Reply
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<a href="https://example.com">link</a> link
<blockquote>quote</blockquote> quote block

Add to the discussion

Questions, fixes, command output, and version notes help keep this guide current.

Verify before posting: