What Gixy reports
[return_bypasses_allow_deny] Return directive bypasses allow/deny restrictions in the same context.
Severity: MEDIUM
Reason: allow/deny do not restrict access to responses produced by
return in the same scope.
The mistake
location /health {
allow 10.0.0.0/8;
deny all;
return 200 "ok";
}
That looks airtight. It is not: anyone on the internet gets 200 ok. The access
list is correct, complete, and never reached.
Why: nginx phases
nginx processes a request through an ordered series of phases. Two of them matter here, and they do not run in the order the config file suggests:
NGX_HTTP_REWRITE_PHASE-- wherereturnandrewriteliveNGX_HTTP_ACCESS_PHASE-- whereallowanddenylive
Rewrite comes first. When return fires it terminates the request and sends the
response immediately; the access phase never runs. Position in the config file is irrelevant
-- moving return below deny all; changes nothing, because nginx is
not reading your block top to bottom at request time.
The fix
Put the restriction somewhere the access phase actually runs, then reach the canned response through an internal redirect. A named location does this cleanly:
http {
open_file_cache max=10000 inactive=60s;
open_file_cache_errors on;
server {
location /health {
allow 10.0.0.0/8;
deny all;
try_files /nonexistent @health;
}
location @health {
return 200 "ok";
}
}
}
try_files runs in the content phase, after access has been evaluated, so a
refused client gets its 403 before the internal redirect is considered.
open_file_cache. A bare try_files
trips a different Gixy check —
try_files_is_evil_too — because every
request pays a filesystem lookup per candidate. Add
open_file_cache max=10000 inactive=60s; and
open_file_cache_errors on; in the http block so you fix this
finding without creating that one.
If the response body is genuinely static, serving a real file is simpler and sidesteps both findings entirely:
location /health {
allow 10.0.0.0/8;
deny all;
root /var/www/health; # contains a file named "index.html"
}
Verify the fix
gixy /etc/nginx/nginx.conf
# From a disallowed address -- this is the whole test:
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/health
# before the fix: 200
# after the fix: 403
When this is a false positive
When the return is the thing you want everyone to get. A redirect block is the
usual example:
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
Nobody is being protected here and no access list is present, so Gixy has nothing to flag.
The finding only appears when allow or deny is in scope -- which
means someone wrote an access rule that they believed was doing something. It is worth a
look every time.
Reference
- return_bypasses_allow_deny in the Gixy documentation — what the check inspects and its options
- All Gixy checks — the full list by severity
- Plugin source — the exact detection logic