Safeguard
Security

Securing Nginx on AWS: The Webinar-Grade Hardening Checklist

Running Nginx on AWS pairs two of the most common infrastructure choices, and this is the hardening walkthrough we would give in a live AWS Nginx webinar.

Yukti Singhal
Platform Engineer
6 min read

Running Nginx on AWS is one of the most common web-serving setups in production, and the security work that a good AWS Nginx webinar covers comes down to a repeatable checklist: terminate TLS correctly, harden the Nginx config, lock down the surrounding AWS network, and keep the binary patched. This guide distills that checklist into something you can apply the same afternoon, whether Nginx runs on an EC2 instance, in a container on ECS, or as an ingress controller in EKS.

Where TLS should terminate

The first architectural decision is where HTTPS ends. On AWS you have three common patterns:

  • At an Application Load Balancer (ALB) in front of Nginx. The ALB terminates TLS using a certificate from AWS Certificate Manager (ACM), and forwards HTTP to Nginx in a private subnet. This is the simplest to operate: ACM renews the certificate automatically, and Nginx never handles a private key.
  • At Nginx directly on the instance. You manage the certificate yourself, typically with Certbot and Let's Encrypt or an imported cert. More control, more responsibility.
  • End-to-end, where the ALB re-encrypts to Nginx. Use this when compliance requires encryption in transit all the way to the origin.

For most teams, terminating at the ALB with an ACM certificate is the right default because it removes private-key handling from your instances entirely. If you terminate at Nginx, enforce modern protocols and disable the old ones:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;

TLS 1.0 and 1.1 are deprecated; do not enable them. Prefer TLS 1.3 where your clients support it.

Harden the Nginx configuration

A default Nginx install leaks information and omits protective headers. Tighten it:

# Stop advertising the exact version
server_tokens off;

# Baseline security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'" always;

server_tokens off stops Nginx from broadcasting its version in responses and error pages, which denies attackers a free fingerprint. The Strict-Transport-Security header (HSTS) forces browsers onto HTTPS. Set a Content-Security-Policy suited to your app; the default-src 'self' above is a strict starting point you loosen deliberately.

Also disable unused HTTP methods and cap request sizes to blunt trivial abuse:

client_max_body_size 10m;
if ($request_method !~ ^(GET|HEAD|POST)$) { return 405; }

Rate limiting and connection limits

Nginx has built-in rate limiting that costs almost nothing to enable and stops a lot of noise, credential-stuffing and scraping in particular:

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

server {
  location /api/ {
    limit_req zone=api burst=20 nodelay;
  }
}

On AWS this complements, rather than replaces, AWS WAF attached to the ALB. WAF handles managed rule groups for common exploits and IP reputation; Nginx limit_req handles per-endpoint request shaping close to the application. Use both.

Lock down the AWS network layer

The most common Nginx-on-AWS mistake is not in the Nginx config at all; it is an overly permissive security group. The web server should be reachable only where it needs to be:

  • If an ALB fronts Nginx, the instance security group should allow inbound traffic only from the ALB's security group, not from 0.0.0.0/0. Reference the ALB security group as the source rather than a CIDR.
  • Expose only 443 (and 80 solely to redirect to 443). Nothing else should be open to the internet.
  • SSH (port 22) should never be open to the world. Use AWS Systems Manager Session Manager for shell access and close 22 entirely, or restrict it to a bastion or your VPN CIDR.
  • Put Nginx instances in private subnets when an ALB is doing public ingress; they do not need public IPs.

Add a redirect so plain HTTP always upgrades:

server {
  listen 80;
  server_name example.com;
  return 301 https://$host$request_uri;
}

Patch the web server and its platform

Nginx itself gets CVEs, and so do the OS packages and any dynamic modules you load. Two practices keep you current:

  • On EC2, automate OS and Nginx package updates (for example with AWS Systems Manager Patch Manager) rather than patching by hand and forgetting.
  • If Nginx runs in a container, the image is a dependency you own. Scan it. Container image scanning in an SCA tool such as Safeguard can flag a known-vulnerable Nginx version or a vulnerable OpenSSL packaged alongside it before the image ships. Rebuild from an updated base image regularly rather than letting a long-lived image drift.

Pin the Nginx version in your Dockerfile so upgrades are explicit and reviewed:

FROM nginx:1.27.0-alpine

The checklist, condensed

  • TLS terminates at the ALB with an ACM cert, or at Nginx with TLS 1.2/1.3 only.
  • server_tokens off, HSTS, and baseline security headers set.
  • Per-endpoint limit_req plus AWS WAF on the ALB.
  • Security group allows inbound only from the ALB; 22 closed, Session Manager for access.
  • Nginx in a private subnet, HTTP redirects to HTTPS.
  • OS and Nginx patched on a schedule; container image scanned and pinned.

Work through it once, template it into your infrastructure-as-code, and every future Nginx deployment starts hardened. Our DevSecOps academy has the companion material on baking these checks into a pipeline.

FAQ

Where should I terminate TLS when running Nginx on AWS?

For most teams, terminate at an Application Load Balancer using an AWS Certificate Manager certificate, which renews automatically and keeps private keys off your instances. Terminate at Nginx directly only when you need that control, and if you do, enable only TLS 1.2 and 1.3.

How do I stop Nginx from exposing its version on AWS?

Set server_tokens off; in the Nginx configuration. This removes the version string from response headers and error pages, denying attackers an easy fingerprint of your exact build. Pair it with keeping the binary patched so a known version would not be exploitable anyway.

What is the most common security mistake with Nginx on AWS?

An overly open security group. The instance often allows inbound from 0.0.0.0/0 when it should allow traffic only from the ALB's security group, and SSH on port 22 is frequently left open to the world. Restrict inbound to the ALB and use Systems Manager Session Manager instead of open SSH.

Do I still need Nginx rate limiting if I use AWS WAF?

Yes, they complement each other. AWS WAF on the ALB handles managed rule groups and IP reputation at the edge, while Nginx limit_req shapes requests per endpoint close to the application. Using both gives you layered protection against scraping and credential stuffing.

Never miss an update

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