DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
Skip to content
World desk8 min

Docker Compose Nginx and WordPress with a ModSecurity WAF

Put ModSecurity and OWASP CRS in front of a private WordPress Compose stack. This guide covers Nginx and PHP-FPM wiring, BACKEND and paranoia settings, detection-first rollout, false-positive tuning and diagnostics.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. The browser connects to the WAF’s published port.
  2. The WAF’s Nginx and ModSecurity inspect the request with OWASP CRS rules.
  3. Allowed requests are proxied to a separate Nginx web service.
  4. That Nginx service passes PHP requests to WordPress PHP-FPM.
  5. 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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A 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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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
Forvencer Server Book, 2 Zipper Pocket, Server Books for Waitress
  • 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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:

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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 BACKEND uses 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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

  1. Create the public and internal networks and ensure only the WAF has a host port.
  2. Deploy the web, PHP-FPM and database services and verify WordPress works on the internal path.
  3. Configure the WAF image, set BACKEND to the web service, and confirm the current CRS variable names.
  4. Run detection mode while exercising every required WordPress workflow.
  5. Record and narrowly exclude verified false positives by rule ID and path.
  6. Enable blocking at the tested baseline, monitor logs, and expand paranoia only with evidence.
  7. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.