This episode explains NGINX hardening: hiding the version, IP-based access control, request and connection rate limiting, security headers, and body size limits for attack mitigation.

An NGINX exposed to the internet is the front door of your application. Without protection, it becomes a target for brute-force attacks, DDoS, and exploits. This Episode 11 covers security hardening and rate limiting — the first line of defense at the NGINX layer.
You'll hide the server version identity, restrict access based on IP, apply rate limiting for requests and connections, install security headers, and limit upload body sizes to defeat large-payload attacks. This is exactly what DevOps teams do before going to production.
The NGINX version shown in the Server header is free information for attackers. Hide it with server_tokens:
http {
server_tokens off;
}Once enabled, the Server: nginx header no longer shows the full version. This small piece of information reduces the attack surface that can be exploited automatically.
For admin areas or internal APIs, restrict who can enter:
location /admin {
allow 192.168.1.0/24;
allow 10.0.0.1;
deny all;
}The allow and deny directives are evaluated in order: the first two allow lines permit the internal subnet and one specific IP, then deny all; rejects everyone else. For internal dashboards, this pattern is far safer than a password alone.
Rate limiting restricts the request speed from a single client. The zone is declared in http, then used in location:
http {
limit_req_zone $binary_remote_addr zone=api_zone:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api_zone burst=20 nodelay;
proxy_pass http://backend_app;
}
}
}limit_req_zone creates an api_zone with a 10 megabyte capacity and a rate of 10 requests per second per IP. The limit_req zone=api_zone burst=20 nodelay; directive allows a burst of 20 requests at once, with the rest immediately rejected with status 503. This is the main bulwark against brute-force logins and API spam.
In addition to requests, also limit the number of parallel connections per IP:
http {
limit_conn_zone $binary_remote_addr zone=conn_zone:10m;
server {
location / {
limit_conn conn_zone 10;
proxy_pass http://backend_app;
}
}
}limit_conn_zone and limit_conn conn_zone 10; restrict a single IP to opening at most 10 parallel connections. This prevents one client from exhausting all worker connections.
Several headers make browsers block attacks more aggressively:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Content-Security-Policy "default-src 'self'" always;
add_header Referrer-Policy "no-referrer" always;X-Frame-Options: SAMEORIGIN prevents clickjacking by blocking embeds in outside frames.X-Content-Type-Options: nosniff prevents browsers from guessing MIME types.Content-Security-Policy restricts which content sources may be loaded.Note: add_header in the server context is not inherited by locations that have their own add_header directive. Put it in an include for consistency — details in episode 14.
Giant uploads can exhaust disk and memory. Set a limit:
server {
client_max_body_size 10M;
}client_max_body_size 10M; rejects requests with a body larger than 10 megabytes with status 413. If your application allows large uploads, adjust this number — but never leave it unlimited.
Episode 11 built NGINX's first-line defense: you hide the server version, restrict IP-based access, apply request and connection rate limiting, install security headers, and limit upload body sizes.
Key takeaways:
server_tokens off; hides the NGINX version from the Server header.allow and deny restrict access based on IP.limit_req_zone and limit_req limit request rate per IP.limit_conn_zone and limit_conn limit parallel connections.client_max_body_size prevents large-payload attacks.In the next episode we'll discuss authentication and authorization in NGINX — HTTP Basic Authentication with htpasswd, subrequest authentication using auth_request, and JWT token validation to secure internal endpoints.