Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Protect and observe Nginx: limit_req, authentication, headers, JSON logs and stub_status

By updated

Improve this page

Nginx logo

Protecting an Nginx that publishes an application means closing what should not be reachable, limiting what is, then making visible what goes through: authentication on the administration area, refusal of hidden files and unneeded methods, rate limiting, security headers, JSON logs and counters. This lesson extends publishing with Nginx: it takes the protection steps of the training and writes them in Nginx syntax, and it lays down only the minimum for rate limiting and observation. Sizing the limits, full observation and diagnosis each have their own lesson, cited where it takes over.

What you will learn

The lesson goes from the most restrictive to the most informative: refuse, limit, announce, then observe.

  • Authenticate access to an administration route.
  • Refuse hidden files and unneeded write methods.
  • Limit the rate with limit_req.
  • Add security headers to responses.
  • Produce JSON logs and expose stub_status locally.

Prerequisites

This lesson assumes you know how to publish an application with Nginx.

How do you protect an administration route?

Require authentication on the route, and only expose it if it has to be exposed. An unprotected /admin route is among the paths that scanners try first. Nginx offers two mechanisms: auth_basic, which compares credentials with a file of hashed passwords, and auth_request, which delegates the decision to an authentication service through a subrequest.

location /admin {
auth_basic "Administration";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:8080;
}

auth_basic is enough for a simple access, provided it goes through HTTPS: credentials travel encoded, not encrypted. auth_request fits when authentication is already handled by another service; the service answers 2xx to allow and 401 or 403 to refuse. The matching module is present in the official packages, but not in every build: check with nginx -V.

What should you refuse right away?

Refuse hidden files and the methods the application does not need. A file whose name starts with a dot (.git, .env) can contain secrets, and a proxy that serves it passes the leak on. A write method on a read-only application has no reason to get through.

location ~ /\. {
deny all;
}
if ($request_method !~ ^(GET|HEAD|POST)$) {
return 405;
}

The first block answers 403 to any path containing a hidden file. The second refuses with 405 the methods that are not listed. Adapt the list to the application: a refusal that is too broad breaks normal requests, and that is precisely what you must check with curl after the change. The /\. pattern also blocks /.well-known/, where ACME authorities come to read the HTTP-01 challenge: if this server obtains its certificates through that challenge, add a location ^~ /.well-known/ that allows it, because the ^~ prefix is chosen before the regular expression.

How do you limit the request rate?

limit_req slows down or refuses clients that exceed a set rate, per address. It is set in two steps: limit_req_zone, at the http level, declares the memory zone, the counting key and the rate; limit_req, in a location, applies the zone with a burst tolerance.

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
server {
location / {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
proxy_pass http://127.0.0.1:8080;
}
}

Here each address can send ten requests per second, with a burst of twenty accepted without delay; beyond that, Nginx answers 429. By default the refusal code is 503: limit_req_status makes it readable for clients and for logs. Behind another relay, the $binary_remote_addr key designates the relay's address, not the client's: the key must match what you actually want to limit.

This is the minimal case. The sizing of rate and burst, the limiting of simultaneous connections, the body size and the choice of a key that a client cannot forge are covered in Nginx limits.

Which security headers should you add?

Add headers that reduce the attack surface on the browser side, with the always parameter so that they also appear on error responses. Without always, Nginx adds the header only on success and redirection codes.

add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "no-referrer" always;
add_header Strict-Transport-Security "max-age=31536000" always;

One pitfall: add_header is not inherited from one level to the next as soon as a lower level declares one. If a location adds its own header, it loses those of the server. So check the headers actually received with curl -I, not the ones you believe you configured.

How do you observe Nginx?

Two sources are enough to start: a structured access log and the stub_status counters. A JSON log can be filtered and aggregated without text parsing, and the counters give the instantaneous load.

log_format json_acces escape=json
'{"heure":"$time_iso8601","client":"$remote_addr",'
'"methode":"$request_method","uri":"$uri","code":$status,'
'"duree":$request_time}';
access_log /var/log/nginx/acces.json json_acces;
server {
listen 127.0.0.1:8081;
location /stub_status {
stub_status;
}
}

escape=json protects the format against special characters in an address. stub_status exposes only global counters: active connections, accepted and handled requests. It details neither response codes nor per-route times, which you must read in the log. Keep this access point on the local interface, as above: it reveals the traffic volume and has no reason to be reachable from outside.

This minimal case stops there. The timing fields that separate Nginx's slowness from the application's, the metrics exporter and the alerts are the subject of Observe Nginx. When a 403, 502 or 504 response appears anyway, the diagnostic approach is in Diagnose Nginx.

Practice: protect an application with Nginx

The lab starts from an Nginx that already publishes the application, but accepts administration requests without protection, serves hidden files and forwards methods the application does not need. It asks you to add the security headers, require the supplied credentials on /admin, refuse hidden files and block the unneeded write methods without breaking the application's normal requests. Validation is an automated check that verifies these refusals and that legitimate requests still work. Practice on observation (JSON log, stub_status, correlation with the upstream) takes place in the lesson Observe Nginx.

Key points

Protecting means refusing the superfluous, then proving the result with curl.

  • auth_basic or auth_request protect the administration area.
  • Refuse hidden files and unneeded methods.
  • limit_req limits per key, and limit_req_status chooses the code.
  • add_header ... always also covers errors, but is not freely inherited.
  • A JSON log and a local stub_status make Nginx observable.

Knowledge check

Knowledge check

Check what you have learned with this interactive quiz

8 questions
8 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn