English | 简体中文
Production-oriented Docker images for running Nginx with the ModSecurity v3 WAF engine. The image builds ModSecurity and the modsecurity-nginx connector from source, compiles the Nginx connector as a dynamic module, and publishes multi-architecture images for linux/amd64 and linux/arm64.
This repository is intentionally small: each supported version pair lives in its own nginx-<version>/mod-<version>/Dockerfile, while build-matrix.json controls which images are currently published by CI.
- Nginx based on the official
nginx:<version>-alpineimage - ModSecurity v3 built from the OWASP ModSecurity source tree
modsecurity-nginxcompiled as an Nginx dynamic module- Runtime libraries required by ModSecurity, including Lua 5.4, LMDB, YAJL, PCRE2, GeoIP, libxml2, and curl
- Module loader file at
/etc/nginx/modules-available/50-modsecurity.conf - Enabled module symlink under
/etc/nginx/modules-enabled/ - ModSecurity
unicode.mappingcopied to/etc/nginx/modsec/
The image provides the WAF engine and Nginx module. It does not bundle a default OWASP Core Rule Set configuration, so production deployments should mount their own ModSecurity configuration and rules.
The current CI build matrix is defined in build-matrix.json:
| Channel | Nginx | ModSecurity | modsecurity-nginx | Dockerfile |
|---|---|---|---|---|
mainline / latest |
1.31.0 |
v3.0.15 |
v1.0.4 |
nginx-1.31.0/mod-3.0.15/Dockerfile |
stable |
1.30.1 |
v3.0.15 |
v1.0.4 |
nginx-1.30.1/mod-3.0.15/Dockerfile |
Older version directories remain in the repository as historical build definitions, but only entries listed in build-matrix.json are built and published by the current GitHub Actions workflow.
Images are published to both Docker Hub and GitHub Container Registry:
docker pull e1saps/nginx-modsecurity:latest
docker pull ghcr.io/apts-1547/nginx-modsecurity:latestUse Docker Hub if you do not have a registry preference. Use GHCR when your deployment workflow already authenticates against GitHub Packages.
Each matrix entry is published with a version tag and a plain Nginx version tag:
| Tag | Example | Meaning |
|---|---|---|
<nginx-version>-<modsecurity-version> |
1.31.0-3.0.15 |
Exact Nginx and ModSecurity pair |
<nginx-version> |
1.31.0 |
Alias for the Nginx version's current ModSecurity pair |
latest |
latest |
Current mainline build |
mainline |
mainline |
Current Nginx mainline build |
stable |
stable |
Current Nginx stable build |
For production, prefer the full version tag, for example:
docker pull e1saps/nginx-modsecurity:1.30.1-3.0.15That avoids accidental upgrades when latest, mainline, or stable move.
Run the latest mainline image:
docker run --rm -p 8080:80 e1saps/nginx-modsecurity:latestThen check the default Nginx page:
curl http://localhost:8080/This confirms that Nginx starts. To actually inspect traffic with ModSecurity, add a ModSecurity configuration and enable it in your Nginx server or location block.
The image places the module loader at:
/etc/nginx/modules-enabled/50-modsecurity.conf
If you replace /etc/nginx/nginx.conf, make sure your custom file includes enabled modules before the events block:
include /etc/nginx/modules-enabled/*.conf;
events {}
http {
server {
listen 80;
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/modsecurity.conf;
location / {
proxy_pass http://app:3000;
}
}
}If you only mount files under /etc/nginx/conf.d/, the default Nginx image entrypoint keeps the base nginx.conf, so the loader behavior depends on that base configuration. When in doubt, provide a complete nginx.conf with the include /etc/nginx/modules-enabled/*.conf; line.
Create a minimal ModSecurity configuration:
# ./modsec/modsecurity.conf
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess Off
SecAuditEngine RelevantOnly
SecAuditLog /var/log/nginx/modsec_audit.log
SecRule ARGS:test "@contains attack" "id:1000,phase:2,deny,status:403,msg:'Test ModSecurity rule'"Create an Nginx config that loads the module and uses that rules file:
# ./nginx.conf
include /etc/nginx/modules-enabled/*.conf;
events {}
http {
server {
listen 80;
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/modsecurity.conf;
location / {
return 200 "nginx-modsecurity is running\n";
}
}
}Run the container:
docker run --rm \
-p 8080:80 \
-v "$PWD/nginx.conf:/etc/nginx/nginx.conf:ro" \
-v "$PWD/modsec:/etc/nginx/modsec:ro" \
e1saps/nginx-modsecurity:stableTest the rule:
curl "http://localhost:8080/?test=ok"
curl -i "http://localhost:8080/?test=attack"The second request should return 403 when the rule is active.
services:
waf:
image: e1saps/nginx-modsecurity:1.30.1-3.0.15
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./modsec:/etc/nginx/modsec:ro
- ./logs:/var/log/nginx
restart: unless-stoppedBuild a specific version from its Dockerfile:
docker build \
-t nginx-modsecurity:1.31.0-3.0.15 \
-f nginx-1.31.0/mod-3.0.15/Dockerfile \
.Run it:
docker run --rm -p 8080:80 nginx-modsecurity:1.31.0-3.0.15For multi-architecture publishing, use Buildx:
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t your-registry/nginx-modsecurity:1.31.0-3.0.15 \
-f nginx-1.31.0/mod-3.0.15/Dockerfile \
--push \
.Use update.sh to create a new version directory from the nearest existing template with the same ModSecurity version:
./update.sh <NGINX_VERSION> <MODSECURITY_VERSION> <MODSECURITY_NGINX_VERSION> [ROLE]Example:
./update.sh 1.31.1 v3.0.15 v1.0.4 mainlineThe script:
- Creates
nginx-<nginx-version>/mod-<modsecurity-version>/Dockerfile - Replaces the Nginx and connector versions in the copied Dockerfile
- Updates the
build_datelabel - Replaces or appends the matching
roleentry inbuild-matrix.json
Valid roles are currently interpreted by CI as:
| Role | Published channel tags |
|---|---|
mainline |
latest, mainline |
stable |
stable |
After running the script, review the generated Dockerfile before committing. Different Nginx or ModSecurity versions sometimes require small build fixes.
The GitHub Actions workflow at .github/workflows/docker-image.yml runs on pushes to master that modify build-matrix.json or versioned Dockerfiles. It can also be started manually with workflow_dispatch.
For each matrix entry, CI:
- Builds
linux/amd64andlinux/arm64images on native runners - Pushes temporary architecture-specific images
- Creates a multi-architecture manifest for Docker Hub and GHCR
- Publishes version tags and channel tags based on the matrix role
- Skips an existing full version tag unless the manual
force_rebuildinput is enabled
.
├── build-matrix.json
├── update.sh
├── nginx-1.31.0/
│ └── mod-3.0.15/
│ └── Dockerfile
├── nginx-1.30.1/
│ └── mod-3.0.15/
│ └── Dockerfile
└── .github/
└── workflows/
└── docker-image.yml
Older nginx-* directories follow the same layout.
- Keep production deployments on exact version tags.
- Mount your own ModSecurity rules and audit-log policy.
- Test rule changes in detection-only mode before switching to blocking mode.
- Treat WAF logs as sensitive data because they may contain request bodies, tokens, or user input.
- Rebuild when upstream Nginx, Alpine, or ModSecurity security updates matter to your deployment.
This project is licensed under the MIT License.