[os-nginx] NAXSI blocked requests return HTTP 200 instead of 403
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 1.2k
- Forks
- 863
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 10
Description
Important notices
Before you add a new report, we ask you kindly to acknowledge the following:
- I have read the contributing guide lines at https://github.com/opnsense/plugins/blob/master/CONTRIBUTING.md
- I have searched the existing issues, open and closed, and I'm convinced that mine is new.
- The title contains the plugin to which this issue belongs
Describe the bug
When NAXSI in the os-nginx plugin blocks a request, the request is correctly blocked internally, but the client receives HTTP 200 OK instead of an appropriate error status such as 403 Forbidden.
The generated nginx configuration uses:
DeniedUrl "/waf_denied.html";
with:
location = /waf_denied.html {
root /usr/local/etc/nginx/views;
access_log /var/log/nginx/waf_denied.access.log main;
}
Because this location serves the static denial page without explicitly returning an error status, the resulting response is HTTP 200.
I do not know a last known working version. The behavior is reproducible on my current installation.
To Reproduce
Steps to reproduce the behavior:
-
Go to Services > Nginx > Configuration > HTTP(S) > Naxsi WAF Rule.
-
Create a Basic Rule matching a test URL, for example:
^/xmlrpc\.php/?$with score
8. -
Add the rule to a Custom Security Policy configured as:
Value: 8 Operator: >= Action: BLOCK -
Enable Security Rules on the applicable nginx Location and assign the Custom Security Policy.
-
Apply the nginx configuration.
-
Request the blocked URL:
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/xmlrpc.php -
The returned HTTP status is:
200 -
The nginx error log simultaneously confirms that NAXSI blocked the original request:
NAXSI_FMT: ... uri=/xmlrpc.php&config=block&...&score0=8&zone0=URL&id0=... -
The request is also written to:
/var/log/nginx/waf_denied.access.logwhere the denial response is logged with status
200.
The blocked request does not reach the upstream application, so the WAF itself is functioning correctly.
Expected behavior
A request blocked by NAXSI should return an HTTP error status, preferably:
403 Forbidden
instead of:
200 OK
The denial page may still be displayed, but it should be returned with the correct HTTP status.
Screenshots
Not required for reproduction. I can provide screenshots of the NAXSI rule, policy and Location configuration if needed.
Relevant log files
Example nginx error log entry:
NAXSI_FMT: ... server=example.com&uri=/xmlrpc.php&config=block&...&score0=8&zone0=URL&id0=...
At the same time, /var/log/nginx/waf_denied.access.log records the blocked request with HTTP status 200.
For example:
"GET /xmlrpc.php HTTP/2.0" 200 ...
The generated configuration contains:
DeniedUrl "/waf_denied.html";
location = /waf_denied.html {
root /usr/local/etc/nginx/views;
access_log /var/log/nginx/waf_denied.access.log main;
}
Additional context
This does not appear to be a WAF bypass. NAXSI correctly detects and blocks the request before it reaches the upstream application.
The problem is the final HTTP status returned to the client. Reporting a blocked request as 200 OK can produce misleading results in vulnerability scanners, monitoring systems, access logs and other automated security tooling.
A possible solution would be for the generated NAXSI denial handler to preserve or explicitly return a 403 status, for example by making the denial location internal and returning HTTP 403 while still optionally serving the existing denial page.
Environment
OPNsense: OPNsense 26.7.2_2-amd64
FreeBSD 15.1-RELEASE-p2
OpenSSL 3.5.7
os-nginx plugin: os-nginx (installed) | 1.36_4
Hardware: DEC850v2
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at Services > Nginx > Configuration > HTTP(S) > Naxsi WAF Rule and inspect how the generated DeniedUrl location is produced. Reproduce with the supplied curl command and compare the nginx error and waf_denied access logs; done means blocked requests return 403 while the denial page may still be displayed and the upstream is not reached.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- nginx
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100