Post

Reverse Proxy WAF with Nginx and ModSecurity

Reverse Proxy WAF with Nginx and ModSecurity

A reverse proxy WAF is a practical defense layer for web apps in a lab. Nginx gives you stability and performance, while ModSecurity provides a rule engine that can inspect HTTP requests for malicious patterns. With the OWASP Core Rule Set (CRS), you get a baseline of protections for common web attacks.

This post shows how to deploy ModSecurity v3 with Nginx, enable CRS, and validate detections with a few attack-like requests.

Architecture overview

Place the WAF in front of your app server. The WAF terminates client connections, inspects requests, and forwards safe traffic to the origin. In a homelab, you can run this on a small VM or container and proxy to a local service.

Install ModSecurity and CRS

On Ubuntu, it is easiest to use the ModSecurity module that is already built for Nginx, or use a prebuilt container if you want speed. The container approach is convenient for labs.

1
docker run --rm -it -p 8080:80 owasp/modsecurity-crs:nginx

This container includes Nginx, ModSecurity v3, and the CRS. It will proxy to a default backend, but you can override it with environment variables.

Basic Nginx configuration

If you want a custom Nginx setup, enable the ModSecurity module and include the CRS rules. The key settings are modsecurity on; and the main rules include file.

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
  listen 80;
  server_name lab.local;

  modsecurity on;
  modsecurity_rules_file /etc/nginx/modsec/main.conf;

  location / {
    proxy_pass http://app:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}

The main.conf should include the CRS rules and set the engine to detection or blocking mode:

1
2
3
SecRuleEngine On
Include /usr/local/owasp-modsecurity-crs/crs-setup.conf
Include /usr/local/owasp-modsecurity-crs/rules/*.conf

Start in DetectionOnly mode to avoid breaking legitimate traffic, then move to blocking once you have tuned the rules.

Test with known payloads

Use curl to send a few requests that should trigger CRS rules:

1
2
curl "http://lab.local/?q=<script>alert(1)</script>"
curl "http://lab.local/?id=1 OR 1=1"

Check the ModSecurity audit log to confirm the rule IDs and messages. A lab WAF is only useful if you know which rules are firing and why.

Request size and body handling

WAFs need to inspect request bodies to detect many attacks, but body inspection has limits. Configure reasonable SecRequestBodyLimit and SecRequestBodyNoFilesLimit values based on your app. If you set these too low, large uploads will bypass inspection or be blocked unexpectedly.

For APIs that accept JSON, enable the JSON body processor. ModSecurity can parse JSON and apply rules to fields. Without this, many rules will not fire on API traffic, which is a common gap in modern apps.

TLS termination and client IPs

If Nginx terminates TLS, make sure your app still sees the real client IP. Use X-Forwarded-For and configure your app or upstream proxy to trust only the WAF’s address. Otherwise, your logs will show the proxy as the source for every request.

In a lab, test this by sending a request and confirming the backend logs the correct client IP. This is critical for incident response because you need accurate attribution.

Tuning and exclusions

CRS is noisy by design. Use rule exclusions for known false positives, but keep them narrow. For example, you can exclude a specific parameter on a specific endpoint instead of turning off the whole rule.

1
SecRuleUpdateTargetById 942100 "!ARGS:search"

Document every exclusion. Untracked exclusions are a common source of security debt.

Performance considerations

ModSecurity adds latency, especially with large rule sets. In a lab you can accept a small penalty, but you should still monitor response times. Use Nginx access logs and compare baseline latency with and without the WAF enabled. If latency spikes, consider enabling CRS in anomaly scoring mode rather than blocking.

Also consider caching. If you cache static assets at the WAF, you reduce the number of requests that need full inspection, which lowers CPU usage. This is a practical way to keep performance stable without weakening the rule set.

Virtual patching workflow

One of the main reasons to run a WAF is virtual patching. If a vulnerability is disclosed and you cannot patch immediately, you can add a temporary rule that blocks the exploit pattern. In a lab, simulate this by adding a custom rule for a specific endpoint and confirm that the request is blocked.

Document these rules and set an expiration date. Virtual patches should be temporary, not permanent replacements for code fixes. A simple spreadsheet or a comment header in your custom rule file is enough.

Rule updates and change control

CRS updates can change rule behavior. Apply updates in a staging environment first, review which rule IDs changed, and run a small set of test requests. This prevents surprises when a new rule suddenly blocks legitimate traffic.

In a lab, you can automate this by running a regression suite of curl tests after updating CRS and comparing which rules fired. This makes your WAF maintenance predictable and measurable.

API-specific protections

Modern apps are heavy on JSON APIs. Enable JSON body parsing and add rules that check for oversized fields, unexpected content types, and missing authentication headers. Many attacks against APIs are logic-based, but you can still catch parameter tampering and injection attempts with careful rules.

If you use GraphQL, consider adding limits for query depth and complexity at the WAF or upstream API gateway. This prevents resource exhaustion attacks that normal CRS rules may miss.

Logging and alerting

ModSecurity writes JSON audit logs if configured. Ship those into your SIEM and add a dashboard that shows rule IDs by frequency. This helps you identify the most common blocks and tune them.

Lab checklist

Use this checklist to validate the WAF in your lab:

  • Send a known XSS or SQLi payload and confirm the rule ID in logs.
  • Verify the backend sees the correct client IP via X-Forwarded-For.
  • Enable JSON parsing and test an API request with malicious fields.
  • Update CRS in staging and re-run your regression tests.

Takeaways

A reverse proxy WAF is a powerful lab component because it forces you to understand how web attacks look at the HTTP layer. With Nginx and ModSecurity, you can stand up a realistic defensive layer quickly, tune it with real traffic, and build alerts that map directly to OWASP threats.

This post is licensed under CC BY 4.0 by the author.