Safeguard
Security Guides•

nginx stub_status: expose the status page safely or not at all

What nginx stub_status reveals, why it needs an allow and deny rule, and how to publish metrics to a monitor without leaving the page open to the internet.

Safeguard Security Team
3 min read

The stub_status page shows live connection counts and request totals. That is harmless for you and useful to an attacker measuring your traffic. Serve it only on a local or internal listener, wrap it in allow and deny rules, and check from outside that it returns an error.

What the page contains

The nginx documentation lists the data: active connections, total accepted connections, total handled connections, total requests, and the Reading, Writing and Waiting counts. Handled usually equals accepted, unless a resource limit such as worker_connections was reached.

None of this is a secret in the sense of a password. But it tells an outsider how busy you are, how close you are to your connection limit, and whether an attack is landing, which is useful for timing one.

How it is enabled

The module is not built by default. It needs --with-http_stub_status_module, though many distribution builds include it. Since version 1.7.5 the directive takes no argument.

location = /basic_status {
    stub_status;
}

Lock it down

The stub_status page does not document access control itself, but you can combine it with allow and deny from the separate access module. Rules are evaluated in order, so allow lines must come before deny all.

location = /basic_status {
    stub_status;
    allow 127.0.0.1;
    allow 10.0.0.0/8;
    deny all;
}

Two cautions.

First, behind a balancer, the address nginx checks may be the balancer's, which is inside your allowed range, and then everyone passes. Check which address the access module sees. The rewriting done by the realip module applies, so a broad trusted range there can undo your allow list. See nginx real_ip.

Second, a wide allow range such as the whole private space is not a boundary if your private network hosts other tenants or untrusted workloads. Narrow it to the monitoring host.

Better: separate listener

Put status on its own server block that listens on loopback or an internal interface only.

server {
    listen 127.0.0.1:8081;
    location = /basic_status { stub_status; }
}

An exporter on the same host reads it. Nothing on the public listener refers to it at all, so a typo in the public config cannot expose it.

Keep the monitor's own credentials and network path in mind too. If an exporter scrapes the page across the network, restrict the allow line to that exporter's single address, and review that list whenever the monitoring host changes.

Check from outside

curl -s -o /dev/null -w '%{http_code}\n' https://example.com/basic_status
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/nginx_status

You want 403 or 404 on both. Also look for other management endpoints in your inventory, including ingress controller metrics ports and admin APIs. Asset discovery is how you find the ones nobody remembers.

Where Safeguard fits

Safeguard provides asset discovery and dependency scanning, which help you find forgotten services and the components they run. A free scan is available at https://gold.safeguard.sh/scan.

Quick check

Why put allow lines before deny all?

nginx evaluates access rules in order and stops at the first match, so deny all first would block everyone.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.