Notes on How to Install Coraza Web Application Firewall (WAF) + OWASP CRS on Debian 13.7.0 Linux Server

From: Turritopsis Dohrnii Teo En Ming <hidden>
Date: 2026-09-27 03:06:56

Subject: Notes on How to Install Coraza Web Application Firewall (WAF) + OWASP CRS on Debian 13.7.0 Linux Server

Good day from Singapore,

Author: Mr. Turritopsis Dohrnii Teo En Ming
Date: 27 Sep 2026 Sunday 12.47 AM
Country: Singapore

Install Debian 13.7.0
======================

nano /etc/apt/sources.list

#deb cdrom:[Debian GNU/Linux 13.7.0 _Trixie_ - Official amd64 DVD Binary-1 with firmware 20260912-09:36]/ trixie contrib main non-free-firmware

apt update

apt full-upgrade -y

apt install -y curl wget git ca-certificates gnupg unzip tar jq

reboot

cat /etc/os-release

Test connectivity to your existing HTTPS server
=================================================

curl -vk https://192.168.88.8 # (This is a VMware ESXi 8.0 Update 3e Server)

Install Go
============

apt install -y golang-go

go version

Then install xcaddy:

GOBIN=/usr/local/bin go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

xcaddy version

Build Caddy with Coraza
===========================

mkdir -p /usr/local/src/caddy-coraza
cd /usr/local/src/caddy-coraza

xcaddy build --with github.com/corazawaf/coraza-caddy/v2

./caddy version

./caddy list-modules | grep -i waf

install -m 755 ./caddy /usr/bin/caddy

/usr/bin/caddy version

Create Caddy user/directories
================================

groupadd --system caddy 2>/dev/null || true
useradd --system \
  --gid caddy \
  --create-home \
  --home-dir /var/lib/caddy \
  --shell /usr/sbin/nologin \
  caddy 2>/dev/null || true
  
  
mkdir -p /etc/caddy
mkdir -p /etc/coraza
mkdir -p /etc/coraza/crs
mkdir -p /var/log/caddy
mkdir -p /var/log/coraza


chown -R root:caddy /etc/caddy
chown -R root:caddy /etc/coraza
chown -R caddy:caddy /var/log/caddy
chown -R caddy:caddy /var/log/coraza

chmod 750 /etc/caddy
chmod 750 /etc/coraza

Download OWASP Core Rule Set
===============================

cd /opt

git clone https://github.com/coreruleset/coreruleset.git coreruleset

cd /opt/coreruleset
git status

Install the CRS files
========================

cp -a /opt/coreruleset/. /etc/coraza/crs/

ls -la /etc/coraza/crs

ls -la /etc/coraza/crs/rules | head -30

chown -R root:caddy /etc/coraza

find /etc/coraza -type d -exec chmod 750 {} \;
find /etc/coraza -type f -exec chmod 640 {} \;

Create Coraza base configuration
====================================

nano /etc/coraza/coraza.conf

SecRuleEngine DetectionOnly

SecRequestBodyAccess On
SecResponseBodyAccess Off

SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072

SecAuditEngine RelevantOnly
SecAuditLogParts ABIJDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/coraza/audit.log

Configure CRS
================

cp /etc/coraza/crs/crs-setup.conf.example \
   /etc/coraza/crs/crs-setup.conf
   
Configure Caddy + Coraza
=========================

nano /etc/caddy/Caddyfile

{
    order coraza_waf first
}

https://192.168.88.7 {

    coraza_waf {
        directives `
            Include /etc/coraza/coraza.conf
            Include /etc/coraza/crs/crs-setup.conf
            Include /etc/coraza/crs/rules/*.conf
        `
    }

    reverse_proxy https://192.168.88.8 {
        transport http {
            tls
            tls_insecure_skip_verify
        }
    }

    log {
        output file /var/log/caddy/access.log
    }
}

***NOTICE: Please note that 192.168.88.7 is the Coraza WAF and 192.168.88.8 is the HTTPS web server it is protecting.***

Better solution: trust the self-signed certificate
====================================================

openssl s_client \
  -connect 192.168.88.8:443 \
  -showcerts </dev/null
  
nano /etc/coraza/backend.crt

-----BEGIN CERTIFICATE-----
---snipped---
-----END CERTIFICATE-----


chmod 644 /etc/coraza/backend.crt

Validate Caddy configuration
===============================

caddy validate \
  --config /etc/caddy/Caddyfile \
  --adapter caddyfile
  
Create systemd service
=========================

nano /etc/systemd/system/caddy.service

[Unit]
Description=Caddy with Coraza WAF
Documentation=https://caddyserver.com/
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
User=caddy
Group=caddy

ExecStart=/usr/bin/caddy run \
  --environ \
  --config /etc/caddy/Caddyfile

ExecReload=/usr/bin/caddy reload \
  --config /etc/caddy/Caddyfile \
  --force

TimeoutStopSec=5s
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full

AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target


chown -R root:caddy /etc/coraza

find /etc/coraza -type d -exec chmod 750 {} \;
find /etc/coraza -type f -exec chmod 640 {} \;


mkdir -p /var/log/caddy
chown -R caddy:caddy /var/log/caddy
chmod 750 /var/log/caddy

chown caddy:caddy /var/log/caddy/access.log
chmod 640 /var/log/caddy/access.log

mkdir -p /var/log/coraza
chown -R caddy:caddy /var/log/coraza
chmod 750 /var/log/coraza

chown caddy:caddy /var/log/coraza/audit.log
chmod 640 /var/log/coraza/audit.log

systemctl daemon-reload
systemctl enable caddy
systemctl start caddy

systemctl status caddy --no-pager -l

journalctl -u caddy -n 100 --no-pager

Confirm ports
==============

ss -lntp | grep -E ':80|:443'

LISTEN 0      4096               *:443             *:*    users:(("caddy",pid=11197,fd=7))
LISTEN 0      4096               *:80              *:*    users:(("caddy",pid=11197,fd=9))

Test normal website traffic
============================

Open https://192.168.88.7 (Coraza WAF) in a Google Chrome web browser.

The site should work normally.

Then watch Coraza/Caddy:

journalctl -u caddy -f

tail -f /var/log/coraza/audit.log

Test SQL injection detection
================================

While still in:

SecRuleEngine DetectionOnly

send a harmless test request:

curl -k 'https://192.168.88.7/?id=1%27%20OR%20%271%27=%271'

tail -100 /var/log/coraza/audit.log

You should see CRS alerts associated with SQL injection.

Test XSS detection
====================

curl -k 'https://192.168.88.7/?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E'

tail -100 /var/log/coraza/audit.log

Again, in DetectionOnly mode the request isn't supposed to be blocked; you're checking that CRS recognizes it.

Turn blocking on
==================

nano /etc/coraza/coraza.conf

Change to

SecRuleEngine On

caddy validate \
 --config /etc/caddy/Caddyfile \
 --adapter caddyfile
 
systemctl reload caddy

Now repeat the SQLi test.

Open https://192.168.88.7/?id=1%27%20OR%20%271%27=%271 in Google Chrome web browser.

Access to 192.168.88.7 was denied
You don't have authorization to view this page.
HTTP ERROR 403

Automatically update OWASP CRS
===============================

<EMPTY>

Create CRS update script
==========================

nano /usr/local/sbin/update-coraza-crs.sh

#!/bin/bash
set -euo pipefail

WORKDIR="/var/tmp/coraza-crs-update"
INSTALLDIR="/etc/coraza/crs"
BACKUPDIR="/etc/coraza/crs-backup"

rm -rf "$WORKDIR"

git clone --depth 1 \
  https://github.com/coreruleset/coreruleset.git \
  "$WORKDIR"

# Preserve local CRS configuration
if [ -f "$INSTALLDIR/crs-setup.conf" ]; then
    cp "$INSTALLDIR/crs-setup.conf" \
       "$WORKDIR/crs-setup.conf"
else
    cp "$WORKDIR/crs-setup.conf.example" \
       "$WORKDIR/crs-setup.conf"
fi

# Backup existing CRS
rm -rf "$BACKUPDIR"
cp -a "$INSTALLDIR" "$BACKUPDIR"

# Install candidate rules
rm -rf "${INSTALLDIR}.new"
cp -a "$WORKDIR" "${INSTALLDIR}.new"

chown -R root:caddy "${INSTALLDIR}.new"
chmod -R g+rX "${INSTALLDIR}.new"

# Temporarily switch directories
mv "$INSTALLDIR" "${INSTALLDIR}.old"
mv "${INSTALLDIR}.new" "$INSTALLDIR"

# Validate complete Caddy/Coraza configuration
if /usr/bin/caddy validate \
    --config /etc/caddy/Caddyfile \
    --adapter caddyfile
then

    systemctl reload caddy

    rm -rf "${INSTALLDIR}.old"

    logger -t coraza-crs-update \
      "OWASP CRS successfully updated"

else

    logger -t coraza-crs-update \
      "CRS update FAILED validation; rolling back"

    rm -rf "$INSTALLDIR"
    mv "${INSTALLDIR}.old" "$INSTALLDIR"

    exit 1
fi

rm -rf "$WORKDIR"


Make executable:

chmod 750 /usr/local/sbin/update-coraza-crs.sh

Automate it with systemd
==========================

Instead of cron, use a systemd timer.

nano /etc/systemd/system/coraza-crs-update.service

[Unit]
Description=Update OWASP Core Rule Set for Coraza

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/update-coraza-crs.sh



nano /etc/systemd/system/coraza-crs-update.timer

[Unit]
Description=Daily OWASP CRS update check

[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
RandomizedDelaySec=30m

[Install]
WantedBy=timers.target


This checks approximately once per day around 03:30.


systemctl daemon-reload

systemctl enable --now coraza-crs-update.timer

systemctl list-timers | grep coraza

Test the updater manually first
====================================

Do not wait until 03:30 for the first run.

systemctl start coraza-crs-update.service

systemctl status coraza-crs-update.service

journalctl \
  -u coraza-crs-update.service \
  -n 100 \
  --no-pager
  
 
systemctl status caddy

Then access the website.

https://192.168.88.7 (Coraza WAF)

Automatic Debian security updates
==================================

You should also keep Debian patched.

apt install -y unattended-upgrades

dpkg-reconfigure unattended-upgrades

Select Yes.

systemctl status unattended-upgrades



That's all.

Regards,

Mr. Turritopsis Dohrnii Teo En Ming
Republic of Singapore
27 Sep 2026 Sunday 1.00 am Singapore Time




Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help