The reliable pattern is to put a ModSecurity plus OWASP Core Rule Set (CRS) container in front of WordPress, publish only the WAF port, and keep the WordPress web server, PHP-FPM process and database on an internal Docker Compose network. Configure the WAF’s BACKEND variable with the Compose service name of the HTTP server behind it. Run the rules in detection mode first, tune narrowly for legitimate WordPress requests, and only then enable blocking.
Antihost offers managed WordPress hosting with free migration, one-click staging and a 14-day free trial.
What the finished request path should look like
A WordPress WAF in Compose is a traffic path, not an add-on installed inside PHP:
As an Amazon Associate I earn from qualifying purchases.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
- The browser connects to the WAF’s published port.
- The WAF’s Nginx and ModSecurity inspect the request with OWASP CRS rules.
- Allowed requests are proxied to a separate Nginx web service.
- That Nginx service passes PHP requests to WordPress PHP-FPM.
- WordPress reads and writes its files and connects to the database over the private Compose network.
Only the WAF should have a host ports mapping. The WordPress web server, PHP-FPM and database can use Compose networking without being reachable directly from the internet. This arrangement prevents an attacker from bypassing the WAF by connecting to an exposed application or database port.
Why PHP-FPM cannot normally be the WAF backend
PHP-FPM speaks FastCGI, not HTTP. The WAF’s BACKEND value must therefore name an HTTP-speaking service, usually the Nginx container that serves WordPress files and forwards dynamic requests to PHP-FPM. A Compose example that points BACKEND directly at a wordpress:php8.3-fpm container is only illustrative; add an Nginx web container unless the selected WordPress image already includes an HTTP server.
#1 Best Overall
Choose and pin the WAF image
The OWASP CRS project publishes official ModSecurity Docker and Core Rule Set images from the coreruleset/modsecurity-crs-docker repository. Image tags, supported environment variables and listening ports can change, so read the current repository documentation before deploying.
Do not copy a version number from an old tutorial. Published search summaries have conflicted about the latest CRS release, and those numbers were not verified against the official release page. Select a release deliberately, test it, then pin the image tag or digest in production instead of using a floating tag.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11A Compose topology that keeps the application private
The following is a wiring example. The WAF image name and tag, database tag and WordPress tag are examples; replace them with versions you have validated. The important security properties are the two networks and the single public port.
services:
waf:
image: owasp/modsecurity-crs:nginx-alpine
ports:
- "8080:8080"
environment:
PORT: 8080
BACKEND: http://web:80
BLOCKING_PARANOIA: 1
DETECTION_PARANOIA: 2
depends_on:
- web
networks:
- public
- internal
web:
image: nginx:alpine
depends_on:
- wordpress
volumes:
- wp_data:/var/www/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
expose:
- "80"
networks:
- internal
wordpress:
image: wordpress:php8.3-fpm
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: change-this-secret
WORDPRESS_DB_NAME: wordpress
volumes:
- wp_data:/var/www/html
expose:
- "9000"
networks:
- internal
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: change-this-secret
MYSQL_ROOT_PASSWORD: change-this-root-secret
volumes:
- db_data:/var/lib/mysql
networks:
- internal
networks:
public:
internal:
internal: true
volumes:
wp_data:
db_data:
Use a secrets mechanism rather than committing passwords in a production Compose file. The database has no ports entry, and the application containers use expose only to document internal ports. A host firewall and TLS termination are still required for a public deployment; the sample’s HTTP port is intended for initial testing.
Rank #2
Configure the Nginx service behind the WAF
The web container needs a server block that serves the shared WordPress directory and sends PHP scripts to the FPM service. A minimal configuration is:
server {
listen 80;
server_name _;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ .php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTP_PROXY "";
fastcgi_pass wordpress:9000;
}
location ~* /. {
deny all;
}
}
The exact Nginx configuration depends on the image and how WordPress files are mounted. Confirm that the WAF can fetch http://web:80/ from its internal network and that Nginx can resolve wordpress as the FPM service name. If either name differs from the Compose service key, change the value; Docker’s internal DNS uses those service names.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Set the WAF’s backend and paranoia controls
BACKEND must name the HTTP service
The CRS installation guidance describes BACKEND as the variable that tells the WAF where to proxy requests. In Compose, use the service name and container port, such as http://web:80. Do not use localhost: inside the WAF container that means the WAF container itself, not the WordPress service. Do not use the host’s public DNS name, which can create hairpin routing or bypass the intended private path.
Use the CRS v4 variable names
In CRS v4, the blocking setting is BLOCKING_PARANOIA; it replaces the older PARANOIA name. DETECTION_PARANOIA lets you test a higher paranoia level without blocking at that level. An example with blocking at level 1 and detection at level 2 is shown in the Compose file. Verify the accepted values and defaults in the documentation for the exact image tag you selected.
Rank #3
- Upgraded Two Zipper Pockets: Forvencer server books feature two secure zipper pockets for better organization of coins, cash, and receipts, ensuring that everything you collect has a safe and secure place
- Smart Storage & Quick Access: Designed with 8 multi-functional compartments, the right side includes a guest receipt pad, while the left has a money pocket, ticket pocket, and credit card slot. Two small clear pockets store bills, receipts, and other visible items. A stitched pen loop ensures you always have your favorite pen ready
- High-quality & Easy to Clean: Crafted from high-quality PU leather with heavy-duty stitching, this server book is built to last. It resists tears, scratches, and its waterproof surface makes cleaning easy with just a damp cloth or a non-chlorine sanitizer
- Perfect Fit for Your Apron: Measuring 5” x 8”, this compact organizer is slightly smaller than other models, making it ideal for bending or sitting while carrying in your server apron. It holds everything a waitress needs—a place for everything
- What's Included: This server organizer comes with multiple open and zippered pockets to store money, receipts, tips, etc. Clear sleeves are perfect for keeping menus or special lists while serving. Available in a variety of colors, allowing you to express yourself even when in uniform
Deploy in detection mode before blocking
Blocking immediately turns normal WordPress operations into an outage when a rule sees an unexpected parameter. Start with the image’s documented detection-only setting, then exercise the site as an administrator and visitor. Keep a terminal attached to the WAF logs:
docker compose up -d
docker compose logs -f waf
Record the rule ID, request path, parameter and authenticated action for every alert. Visit the homepage, log in, edit a post, upload media, open the block editor, save settings, run plugin and theme updates, use the REST API and perform any scheduled or webhook actions your site needs. This gives you a baseline of legitimate traffic before enforcement.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →After the baseline is clean, enable blocking at the lowest tested level (commonly level 1), redeploy, and repeat the same workflow. Use DETECTION_PARANOIA to observe a higher level while level 1 blocks. Increase blocking paranoia only after reviewing its alerts and capacity for false positives.
Test the path from outside and inside
First check that the public entry point returns WordPress content:
Rank #4
curl -i http://localhost:8080/
Then send requests that should produce CRS alerts, using a disposable test site and never against a third-party server:
curl -i -G http://localhost:8080/
--data-urlencode 'q=<script>alert(1)</script>'
curl -i http://localhost:8080/?id=1%20OR%201%3D1
Inspect both the response and docker compose logs waf. A blocked request should be explainable by a rule ID in the WAF log; an allowed request should still appear in detection logs when the corresponding rule fires. Also test a large multipart upload, JSON REST requests and authenticated admin requests because those are common places for content and size limits to differ.
Tune WordPress false positives without weakening the whole CRS
WordPress legitimately sends HTML-like content, serialized data, JSON and long query strings. Admin screens, block-editor requests, plugin updates and XML-RPC traffic are especially likely to resemble attack payloads. A false positive is not a reason to disable an entire CRS rule group.
- Capture the exact URL path, HTTP method, parameter and CRS rule ID from the log.
- Confirm that the request is a required WordPress operation and not a compromised account or plugin.
- Add the narrowest exclusion available: limit it to the affected rule ID and path or parameter.
- Retest the original request and a known malicious request after the change.
- Keep exclusions in version-controlled configuration so an image upgrade does not silently erase them.
Path-specific exclusions are safer than globally disabling a rule group. Treat XML-RPC as a deliberate feature: if the site does not need it, disable it at the application or web-server layer; if it does, test legitimate clients before changing WAF rules. Apply the same discipline to admin-ajax.php, REST endpoints and plugin upload handlers.
Health checks and failure diagnosis
The WAF returns a gateway error
- Check that
BACKENDuses the web service name, not the FPM service. - From the WAF container, verify that the backend port is listening and that Docker DNS resolves the name.
- Read the web container logs for Nginx syntax errors or a failed FastCGI connection.
- Confirm that the shared WordPress volume is mounted in both the web and FPM containers.
WordPress is reachable without the WAF
Remove any ports mappings from the web, FPM and database services. They should be attached only to the internal network. If another reverse proxy or cloud load balancer exists, point it at the WAF service and ensure its health check cannot select the private web container directly.
Every request is blocked after enabling enforcement
Return temporarily to detection mode, identify the first blocking rule in the WAF log, and reproduce the request with a clean browser session. Check CRS and image documentation for required initialization variables, then add a narrow exclusion only when the request is demonstrably legitimate. Do not respond by turning off ModSecurity or all CRS rules.
Updates break after an image refresh
Compare the old and new image tag, review CRS release notes, and re-run the administrator workflow. Variable names and defaults can change; in particular, legacy configurations using PARANOIA need to be translated to the CRS v4 controls. Keep the tested image pinned and promote upgrades through staging.
Quick Recap
Production checklist
- Publish only the WAF port; keep application and database services on the internal network.
- Terminate TLS at the WAF or a trusted upstream and preserve the original host and client-IP headers according to your proxy design.
- Use strong, externally managed database credentials and back up both the database and WordPress files.
- Pin a CRS image tag or digest after checking the official release information.
- Run detection mode first, document normal traffic, then enable blocking at a tested paranoia level.
- Monitor WAF, Nginx, PHP and database logs together; a 403, 502 and application error have different causes.
- Review exclusions whenever WordPress, a plugin, a theme or the CRS image changes.
- Retest login, media upload, block editing, REST API calls, plugin updates and any XML-RPC client after each change.
Recommended operating sequence
- Create the public and internal networks and ensure only the WAF has a host port.
- Deploy the web, PHP-FPM and database services and verify WordPress works on the internal path.
- Configure the WAF image, set
BACKENDto the web service, and confirm the current CRS variable names. - Run detection mode while exercising every required WordPress workflow.
- Record and narrowly exclude verified false positives by rule ID and path.
- Enable blocking at the tested baseline, monitor logs, and expand paranoia only with evidence.
- Pin and periodically test image upgrades rather than accepting unreviewed floating-tag changes.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




