Key Takeaways
- Hide version info:
server_tokens offprevents version disclosure. Hide OS withproxy_hide_header X-Powered-By. - Rate limit everything public:
limit_req_zone + limit_reqin 5 minutes of configuration dramatically reduces brute-force and credential-stuffing attack effectiveness. - Security headers are free: HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy — these headers cost nothing to add and block entire classes of attacks.
- TLS 1.2 + 1.3 only: Disable TLS 1.0 and 1.1 which have known vulnerabilities. The
ssl_protocols TLSv1.2 TLSv1.3directive is the only supported configuration in 2026.
Introduction
Direct Answer: How do I harden Nginx against attacks on Ubuntu 24.04 in 2026?
The six most impactful Nginx hardening changes are: (1) server_tokens off in nginx.conf to hide version; (2) limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s in the http block with limit_req zone=general burst=60 nodelay in location blocks for rate limiting; (3) ssl_protocols TLSv1.2 TLSv1.3 to disable weak TLS; (4) security headers via add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always, add_header X-Frame-Options SAMEORIGIN always, and add_header X-Content-Type-Options nosniff always; (5) client_max_body_size 10M to limit request sizes; (6) block common attack patterns with location ~* \.(php|asp|aspx|cgi)$ { return 404; }. Apply all changes in /etc/nginx/nginx.conf and /etc/nginx/sites-available/, test with sudo nginx -t, and reload with sudo systemctl reload nginx.
Part 1: Global Security Configuration
sudo tee /etc/nginx/conf.d/security.conf << 'EOF'
# ── Token and Version Hiding ──────────────────────────────────────────────
server_tokens off; # Return "Server: nginx" not "Server: nginx/1.27.3"
# ── Rate Limiting Zones ───────────────────────────────────────────────────
# Zone for general web traffic (30 req/sec per IP)
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;
# Strict zone for auth endpoints (5 req/min per IP)
limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m;
# Connection limit zone
limit_conn_zone $binary_remote_addr zone=addr:10m;
# ── Request Size Limits ───────────────────────────────────────────────────
client_max_body_size 10M; # Max upload size
client_body_buffer_size 128k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
# ── Timeout Protection ────────────────────────────────────────────────────
client_body_timeout 12s;
client_header_timeout 12s;
keepalive_timeout 15s;
send_timeout 10s;
# ── Buffer Overflow Protection ────────────────────────────────────────────
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
EOF
sudo nginx -t && sudo systemctl reload nginx
Part 2: Security Headers
sudo tee /etc/nginx/conf.d/security-headers.conf << 'EOF'
# Applied to all HTTPS virtual hosts via include or directly in server blocks
# ── Core Security Headers ─────────────────────────────────────────────────
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# ── Content Security Policy (start with report-only) ──────────────────────
# STEP 1: Monitor violations without blocking (start here)
add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; report-uri /csp-report" always;
# STEP 2: After 2 weeks with no legitimate violations, switch to enforcement:
# add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:" always;
EOF
sudo nginx -t && sudo systemctl reload nginx
# Verify headers are present
curl -sI https://yourdomain.com | grep -E "Strict|X-Frame|X-Content|Referrer|Permissions|Content-Security"
Expected output:
strict-transport-security: max-age=31536000; includeSubDomains; preload
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
permissions-policy: geolocation=(), microphone=(), camera=()
content-security-policy-report-only: default-src 'self'; ...
Part 3: Rate Limiting in Practice
# Apply rate limiting to a virtual host
sudo tee /etc/nginx/sites-available/hardened-app << 'EOF'
server {
listen 443 ssl;
http2 on;
server_name yourapp.example.com;
ssl_certificate /etc/letsencrypt/live/yourapp.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourapp.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
# Include global security headers
include conf.d/security-headers.conf;
# ── General API — 30 req/s with burst ────────────────────────────────
location /api/ {
limit_req zone=general burst=60 nodelay;
limit_req_status 429;
proxy_pass http://127.0.0.1:3000;
proxy_set_header X-Real-IP $remote_addr;
}
# ── Auth endpoints — 5 req/min (strict) ──────────────────────────────
location /api/auth/ {
limit_req zone=auth burst=10;
limit_req_status 429;
proxy_pass http://127.0.0.1:3000;
proxy_set_header X-Real-IP $remote_addr;
}
# ── Connection limit (prevent connection exhaustion) ──────────────────
location /api/upload/ {
limit_conn addr 10; # Max 10 simultaneous connections per IP
limit_req zone=general burst=5;
client_max_body_size 100M;
proxy_pass http://127.0.0.1:3000;
}
# ── Block common attack vectors ───────────────────────────────────────
location ~* \.(php|asp|aspx|cgi|env|git|htaccess)$ {
return 404;
}
location ~ /\. {
deny all;
return 404;
}
location ~* /(wp-admin|wp-login|xmlrpc\.php|wp-config) {
return 404;
}
}
server {
listen 80;
server_name yourapp.example.com;
return 301 https://$host$request_uri;
}
EOF
sudo ln -sf /etc/nginx/sites-available/hardened-app /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Part 4: Modern TLS Configuration
sudo tee /etc/nginx/conf.d/ssl-modern.conf << 'EOF'
# TLS 1.2 + 1.3 only — TLS 1.0 and 1.1 are disabled
ssl_protocols TLSv1.2 TLSv1.3;
# Modern cipher suite (TLS 1.2 fallback)
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;
# Session management
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off; # Disable — tickets have forward secrecy issues
# OCSP Stapling — improves TLS handshake performance
ssl_stapling on;
ssl_stapling_verify on;
resolver 9.9.9.9 1.1.1.1 valid=300s;
resolver_timeout 5s;
EOF
sudo nginx -t && sudo systemctl reload nginx
# Test TLS configuration
curl -sI --tlsv1.2 https://yourdomain.com | head -3
curl -sI --tlsv1.0 https://yourdomain.com 2>&1 | head -3 # Should fail
Expected output:
HTTP/2 200
HTTP/2 200
curl: (35) error:1404B42E:SSL routines:ST_CONNECT:tlsv1 alert protocol version
TLS 1.2 works; TLS 1.0 is correctly rejected.
Part 5: DDoS Mitigation
sudo tee /etc/nginx/conf.d/ddos-protection.conf << 'EOF'
# Limit simultaneous connections per IP
limit_conn_zone $binary_remote_addr zone=per_ip:10m;
limit_conn per_ip 20;
# Slow down attackers with binary logging (faster than string)
# Block common DDoS user agents (basic bot blocking)
map $http_user_agent $bad_bot {
default 0;
~*masscan 1;
~*zmeu 1;
~*nikto 1;
~*sqlmap 1;
"" 1; # Block empty user agents
}
# Applied in server block:
# if ($bad_bot) { return 444; } # 444 = close connection without response
# Geographic blocking (if needed — requires GeoIP module)
# geo $blocked_country { default 0; CN 1; RU 1; } # Example
EOF
sudo nginx -t && sudo systemctl reload nginx
Part 6: Security Audit
# Run a quick security audit
echo "=== NGINX SECURITY AUDIT ==="
echo ""
echo "[ Server version disclosure ]"
curl -sI http://localhost | grep "^Server:" | sed 's/^/ /'
echo " (Should show 'Server: nginx' not version number)"
echo ""
echo "[ Security headers present ]"
curl -sI https://yourdomain.com 2>/dev/null | \
grep -iE "strict-transport|x-frame|x-content-type|referrer-policy|content-security" | \
sed 's/^/ ✓ /'
echo ""
echo "[ Rate limit zones defined ]"
nginx -T 2>/dev/null | grep "limit_req_zone" | sed 's/^/ /'
echo ""
echo "[ TLS protocols enabled ]"
nginx -T 2>/dev/null | grep "ssl_protocols" | head -1 | sed 's/^/ /'
echo ""
echo "[ Common attack paths blocked ]"
for path in /wp-login.php /.env /.git/config /admin/config.php; do
code=$(curl -sI -o /dev/null -w "%{http_code}" "http://localhost${path}")
icon="✓"; [ "$code" = "200" ] && icon="✗"
echo " $icon $path → HTTP $code"
done
Expected output (hardened server):
=== NGINX SECURITY AUDIT ===
[ Server version disclosure ]
Server: nginx
(Should show 'Server: nginx' not version number)
[ Security headers present ]
✓ strict-transport-security: max-age=31536000; includeSubDomains; preload
✓ x-frame-options: SAMEORIGIN
✓ x-content-type-options: nosniff
✓ referrer-policy: strict-origin-when-cross-origin
[ Rate limit zones defined ]
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;
limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m;
[ TLS protocols enabled ]
ssl_protocols TLSv1.2 TLSv1.3;
[ Common attack paths blocked ]
✓ /wp-login.php → HTTP 404
✓ /.env → HTTP 404
✓ /.git/config → HTTP 404
✓ /admin/config.php → HTTP 404
Conclusion
Nginx is now hardened: version disclosure eliminated, rate limiting protecting all endpoints, security headers blocking browser-level attacks, TLS 1.0/1.1 disabled, and common attack vectors returning 404. This configuration passes the OWASP Nginx Security Checklist and satisfies most security audit requirements.
See UFW Firewall Tutorial 2026 for the firewall layer protecting the server running this Nginx, and Nginx Reverse Proxy Tutorial 2026 for the proxy configuration this hardening layer sits on top of.
People Also Ask
What is the difference between limit_req and limit_conn in Nginx?
limit_req limits the request rate — how many requests per second/minute an IP can make. limit_conn limits simultaneous open connections from an IP. Both protect against different attacks. limit_req stops request flooding (API abuse, brute force). limit_conn stops connection exhaustion attacks (opening thousands of connections and holding them open). Use both: limit_req zone=general burst=20 nodelay for rate limiting plus limit_conn per_ip 20 for connection limiting.
Should I enable server_tokens off in production?
Yes, always. Security through obscurity isn’t a complete defence, but removing the exact Nginx version from response headers eliminates automated version-specific exploit scanning. When scanners can’t identify the exact version, they either skip the server or have to try all known vulnerabilities, dramatically reducing automated attack success rates. Enable it with one line in nginx.conf: server_tokens off;.
Part 12: Building a Hardened NGINX Architecture
A hardened NGINX deployment is more than configuration values. It is a layered architecture designed to stop threats before they reach the application.
12.1 Network segmentation
Put NGINX in a DMZ or gateway network segment. Keep backend application servers on an isolated internal network. Only allow the ports NGINX needs to reach the app servers.
12.2 Reverse proxy and application boundary
Use NGINX as the sole external face of the service. Do not expose backend app ports directly. This gives you a single, audited boundary for TLS, access control, and request filtering.
12.3 Least-privilege process model
Run NGINX as a dedicated unprivileged user. Do not run it as root except for binding privileged ports. Switch to a lower-privilege user immediately after startup.
user nginx;
worker_processes auto;
12.4 Dedicated TLS terminator
Make NGINX the TLS terminator, not your app. Terminate TLS on NGINX and forward traffic internally over a private network. This allows you to centralize certificate management and issue strong TLS policies.
Part 13: TLS Hardening Best Practices
TLS is the foundation of secure HTTPS. In 2026, older ciphers and weak TLS versions are no longer acceptable.
13.1 Strong TLS versions and ciphers
Only allow TLS 1.3 and use a short cipher suite list. Example:
ssl_protocols TLSv1.3;
ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
Avoid legacy ciphers, weak DH groups, and TLS 1.2 unless you must support very old clients.
13.2 Forward secrecy
Enable forward secrecy by preferring ephemeral key exchanges.
ssl_prefer_server_ciphers on;
This protects captured traffic even if the server key is later compromised.
13.3 TLS certificate management
Use automated certificate issuance and renewal. For internal deployments, a private PKI or ACME server is ideal. For public-facing services, use a trusted CA and automate via Certbot, acme.sh, or an internal ACME endpoint.
13.4 HTTP Strict Transport Security
Deploy HSTS with a long max-age and include subdomains when appropriate.
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
Use preload only after verifying all subdomains support HTTPS.
Part 14: Access Control and Authentication
NGINX can enforce access control before requests ever reach your application.
14.1 IP allow/deny
Use allow/deny blocks for internal admin endpoints and API entry points.
location /admin {
allow 10.0.0.0/8;
deny all;
}
14.2 Basic auth for staging and internal services
For non-production environments, protect the site with HTTP Basic authentication to prevent accidental indexing or exposure.
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
14.3 Client certificate authentication
For highly sensitive services, require client certificates.
ssl_verify_client on;
ssl_client_certificate /etc/nginx/ca.crt;
This is especially useful for internal APIs and privileged admin consoles.
Part 15: WAF and Request Filtering
A web application firewall adds a second layer of defense.
15.1 ModSecurity
Use ModSecurity with NGINX to block common web attacks. Deploy a curated rule set and keep it updated.
15.2 Request body limits
Protect against large payload attacks by limiting request sizes.
client_max_body_size 10m;
For file uploads, tune the limit to the expected maximum and reject anything larger.
15.3 Deny suspicious user agents
Block requests from known bad or empty user agents when appropriate. This is a lightweight filter that catches some automated probes.
Part 16: Logging and Monitoring
Security is only effective if you can see what is happening.
16.1 Structured access logs
Use JSON structured logs with important metadata.
log_format json '{"time":"$time_iso8601","remote_addr":"$remote_addr","request":"$request","status":"$status","request_time":$request_time}';
access_log /var/log/nginx/access.json json;
This makes logs easier to ingest into SIEM and local analytics tools.
16.2 Error logging and audit trails
Capture warnings and errors at the correct level.
error_log /var/log/nginx/error.log warn;
Retain logs long enough for incident investigation and correlate them with upstream application logs.
16.3 Health checks and alerting
Monitor NGINX health endpoints and expose basic status information internally.
location /nginx_status {
stub_status on;
allow 127.0.0.1;
deny all;
}
Use alerts for upstream failures, backend errors, and abnormal request rates.
Part 17: Configuration Management and Review
Safe NGINX deployments require repeatable configuration and review.
17.1 Immutable config artifacts
Treat NGINX config as code. Store it in git, deploy it from the repository, and use immutable artifacts where possible.
17.2 Syntax checks and staging
Always run nginx -t before reloading config. Deploy first to staging and validate with smoke tests.
17.3 Change control
Review every security-related config change. Use peer review and document the expected behavior.
Part 18: Runtime Hardening and OS Security
NGINX security begins at the operating system level.
18.1 Minimal base image
Use a minimal operating system or container image. Remove unnecessary packages and avoid large distributions when you can.
18.2 OS-level mitigations
Enable kernel hardening features such as sysctl restrictions on core dumps, IP forwarding, and packet redirects.
Example /etc/sysctl.conf settings:
net.ipv4.ip_forward = 0
net.ipv4.conf.all.rp_filter = 1
vm.panic_on_oom = 0
18.3 File and directory permissions
Lock down NGINX config and certificate files.
chmod 600 /etc/nginx/ssl/*
chmod 640 /etc/nginx/nginx.conf
Only the NGINX user and administrators should be able to read sensitive files.
Part 19: Incident Response and Recovery
Prepare for the day when the security boundary is tested.
19.1 Compromise plan
Document the steps to recover from a suspected compromise. Include certificate rotation, configuration rollback, and filesystem checklist.
19.2 Backup config and certs
Regularly back up NGINX configuration, TLS certificates, and log archives. Keep backups off-host if possible to protect against ransomware.
19.3 Post-incident analysis
If an incident occurs, review the timeline, identify the root cause, and update hardening controls accordingly.
Part 20: Future-Proofing NGINX Security
Security is an ongoing process.
20.1 Regular audits
Audit your NGINX deployment annually or whenever a major architecture change occurs. Include TLS, headers, WAF rules, and access controls.
20.2 Upgrade planning
Plan NGINX upgrades carefully. Newer versions bring improved TLS support, bug fixes, and better performance. Test upgrade paths in a staging environment before rollout.
20.3 Keep threat models current
Update your threat model as your application and network topology evolve. Security hardening should reflect the current attack surface, not an old diagram.
Part 21: Advanced NGINX Security Controls
NGINX can provide both defensive and detective controls when configured carefully.
21.1 Rate limiting and connection limits
Prevent abuse and slow-burn attacks by limiting the number of requests and connections per client.
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
location / {
limit_req zone=req_limit burst=20 nodelay;
limit_conn conn_limit 10;
}
}
This helps protect the application from both bursts and slow clients.
21.2 Request body inspection
Inspect the request body when you need to catch malicious payloads before proxying to the app.
Use ModSecurity or NGINX’s built-in lua module for content-based filters.
21.3 Header whitelisting and sanitisation
Strip or rewrite unsafe headers before they reach your backend.
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_hide_header X-Powered-By;
Only pass headers that are necessary for the upstream service.
21.4 Security response headers
Add response headers that harden client behavior.
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "same-origin" always;
add_header Content-Security-Policy "default-src 'self'" always;
These headers reduce clickjacking, MIME-sniffing, and cross-site risks.
Part 22: DDoS and High-Volume Protection
Mitigating denial-of-service threats is an important part of self-hosted security.
22.1 Connection and request throttling
Use limit_req and limit_conn to enforce per-client quotas. Tune burst values carefully so valid clients are not dropped.
22.2 Slowloris protection
Enable sensible timeouts to avoid holding connections open indefinitely.
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 15s;
22.3 Use a caching layer for static assets
Serve static files directly from NGINX with aggressive caching headers. This reduces load on backend servers and improves resilience.
22.4 Upstream server health checks
Configure active health checks so NGINX automatically stops routing traffic to unhealthy backends.
upstream backend {
server 10.0.0.10:8080;
server 10.0.0.11:8080;
health_check;
}
Part 23: Container and Image Security
Modern deployments often run NGINX in containers. Hardening the container matters.
23.1 Minimal base images
Use minimal base images such as nginx:alpine or a distroless variant. Remove all unnecessary packages to reduce the attack surface.
23.2 Immutable container contents
Build container images with the NGINX config baked in and no runtime editing. This makes deployments reproducible.
23.3 Runtime restrictions
Use container runtime security settings such as seccomp profiles and read-only root filesystems.
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
allowPrivilegeEscalation: false
23.4 Image scanning
Scan images for vulnerabilities before deploying them. Use local or CI/CD scanning tools to catch outdated libraries and CVEs.
Part 24: Change Management and Rollback
NGINX config changes can have immediate impact. Manage them carefully.
24.1 Canary deploys for config changes
Roll out configuration changes to a small subset of traffic or servers first. Validate behavior before promoting the change cluster-wide.
24.2 Automated rollback
If a new config causes errors, automatically rollback to the previous known-good version. Keep config history accessible.
24.3 Document security exceptions
If you need to weaken a security header or allow a legacy cipher, document the exception and the compensating controls.
Part 25: Compliance and Audit Readiness
A hardened NGINX deployment should support audits.
25.1 Configuration documentation
Store documentation of TLS policies, access controls, and firewall rules with the deployment artifacts.
25.2 Audit logs
Ensure audit logs exist for administrative config changes and certificate rotations.
25.3 Periodic review
Review NGINX security settings regularly and update them based on new vulnerabilities and best practices.
Part 26: Security Testing and Validation
A hardened NGINX configuration must be tested as thoroughly as code.
26.1 Automated security scans
Run automated security scanners against the deployed NGINX endpoint. Tools like OpenVAS and Nikto can identify missing headers, weak TLS ciphers, and open ports.
26.2 TLS validation
Regularly validate TLS configuration with tools such as Mozilla Observatory, SSL Labs, or internal TLS scanners. Capture results in a dashboard and alert on regressions.
26.3 Fuzz testing
Fuzz the HTTP interface to find parser or proxy edge cases. A self-hosted deployment should be tested against malformed request streams to ensure NGINX does not crash or expose internal behavior.
26.4 Configuration drift detection
Detect drift between the deployed NGINX config and the source-of-truth repository. Use checksums or GitOps tooling to alert if a manual change is made on the host.
Part 27: Response Handling and Error Management
How NGINX handles errors can reveal information and affect security.
27.1 Custom error pages
Serve custom error pages for 4xx and 5xx responses. Avoid exposing stack traces or backend internal messages.
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
27.2 Error logging policies
Log the minimum sensitive information needed for debugging. Avoid logging full request bodies for error cases unless strictly necessary.
27.3 Rate-limited error responses
When errors spike, return the same generic response for repeated threat patterns. This prevents attackers from using error messages for reconnaissance.
Part 28: Metrics and Observability at Scale
Monitoring NGINX is about both load and security.
28.1 Prometheus metrics
Expose NGINX metrics for request rates, latency, response codes, and upstream health. Use a dedicated metrics exporter if needed.
28.2 Log aggregation
Aggregate access and error logs centrally. Correlate NGINX logs with upstream application logs and network logs for full-stack incident analysis.
28.3 Alert thresholds
Configure alerts for abnormal spikes in 4xx/5xx rates, backend failures, and server load. Use rate-of-change thresholds to catch sudden issues.
Part 29: Operational Hardening for CI/CD
Integrate NGINX security into your deployment pipeline.
29.1 Pre-deploy checks
Run nginx -t, config linters, and security header validators in CI before merging changes. Fail the build for invalid or insecure settings.
29.2 Canary configuration rollout
Deploy NGINX config changes to a small subset of servers first and validate traffic behavior. Use feature flags or traffic splits where possible.
29.3 Automated rollback criteria
Define objective rollback criteria: high error rate, degraded latency, or failed health checks. Automate rollback when these criteria are met.
Part 30: Governance and Documentation
Make security operationally sustainable.
30.1 Configuration whitelists
Document approved NGINX modules, TLS ciphers, and header policies. Use a whitelist approach rather than allowing arbitrary changes.
30.2 Incident review process
After a security or availability incident, perform a blameless postmortem. Include NGINX-specific findings, such as config gaps or monitoring blind spots.
30.3 Security training
Ensure your operations team understands NGINX configuration semantics, TLS trust chains, and API gateway behavior. Invest in internal training or runbook reviews.
Further Reading
- How to Install Nginx on Ubuntu 24.04 LTS — base installation before hardening
- Nginx Reverse Proxy Tutorial 2026 — proxy configuration this hardening applies to
- UFW Firewall Tutorial 2026 — OS-level firewall alongside Nginx hardening
- Ubuntu 24.04 LTS Server Setup Checklist — full server security checklist
Tested on: Ubuntu 24.04 LTS (Hetzner CX22). Nginx 1.27.3. Last verified: April 29, 2026.