What Gixy reports
[status_page_exposed] stub_status is publicly accessible without IP restrictions.
Severity: MEDIUM
Reason: no allow directives found; no "deny all" found
What leaks
The endpoint is small. That does not make it harmless:
Active connections: 291
server accepts handled requests
16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106
From those seven numbers an attacker learns:
- Your traffic volume -- request totals over two samples give a precise rate.
- Your capacity headroom -- active connections against your published
worker_connectionsshows how much load it would take to exhaust you. - Whether an attack is working -- a live feedback channel during a denial-of-service attempt, which is the genuinely dangerous one. Polling this endpoint tells them when to stop scaling up.
- Your deployment rhythm --
acceptsandhandleddiverging, or counters resetting, marks reloads and restarts.
The mistake
server {
listen 443 ssl;
server_name example.com;
location /nginx_status {
stub_status;
}
}
Obscurity is not a fix. /nginx_status, /status,
/stub_status and /basic_status are in every scanner's wordlist, and
an unusual path is one directory-brute-force away.
The fix
Restrict by address, and close the list with deny all;:
server {
listen 443 ssl;
server_name example.com;
location /nginx_status {
stub_status;
allow 127.0.0.1;
allow 10.0.0.0/8; # monitoring subnet
deny all;
access_log off;
}
}
Gixy wants to see both an allow and a deny all; in the same scope.
An allow on its own leaves the list open at the bottom -- the
allow_without_deny problem.
Better: keep it off the public listener entirely
Address filtering is one misconfigured real_ip setting away from failing open.
A listener that is not reachable from the network at all has no such failure mode. A Unix
socket is the strongest version of this, and Gixy explicitly exempts a server block whose
every listen is a Unix socket:
server {
listen unix:/var/run/nginx-status.sock;
server_name localhost;
location /nginx_status {
stub_status;
access_log off;
}
}
Point your monitoring agent at the socket and nothing traverses the network. Most agents
support this; curl --unix-socket /var/run/nginx-status.sock http://localhost/nginx_status
confirms it by hand.
listen
127.0.0.1:8080; is genuinely unreachable from outside, but Gixy's exemption covers
Unix sockets only, so it will still report the check. That is a real false positive —
either switch to a socket as above, or keep allow 127.0.0.1; deny all; in the
location so the finding clears on its own terms.
Behind a CDN or load balancer, allow lists see the proxy
If your allow list contains the address of your own load balancer, every request through
it matches -- including the attacker's. Configure set_real_ip_from and
real_ip_header so the rules evaluate the true client, or use the localhost
listener above.
Verify the fix
gixy /etc/nginx/nginx.conf
# From outside -- the only test that counts:
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/nginx_status
# expect 403
# From the monitoring host:
curl -sS https://example.com/nginx_status
# expect the counters
When this is a false positive
When the enforcement is real but lives outside this config -- a firewall rule, a security group, or a listener on an interface that is not routable from the internet. Gixy reads the config file and cannot see any of those. The finding is still worth keeping honest: confirm the external control actually exists rather than assuming it does, because "it is on an internal network" is the assumption that gets audited most often and holds least often.
Reference
- status_page_exposed 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