nginx TLS Hardening
You harden nginx from an insecure default state (self-signed snakeoil cert + TLSv1 enabled + permissive ciphers + no security headers) into a modern TLS configuration. 7 tasks, about 60 minutes. Note: We use a self-signed certificate — browser warnings are expected here (in production you'd use Let's Encrypt, that's a separate module).
Harden nginx TLS configuration
Hardening nginx & TLS
TLS (Transport Layer Security, the successor to SSL) is the encryption behind HTTPS. It ensures nobody between browser and server can eavesdrop or alter data. A web server like nginx can, however, configure TLS insecurely — with outdated protocols, weak ciphers and missing protective headers.
Key terms
- Certificate: proves the server's identity. Here you use a self-signed cert — browser warnings are expected (production uses Let's Encrypt).
- TLS version:
TLSv1.0/1.1are broken; onlyTLSv1.2andTLSv1.3are safe. - Cipher suite: the concrete encryption algorithms.
ECDHE+GCMsuites are safe. - Security headers:
HSTSenforces HTTPS,CSPlimits what the browser may load (protection against XSS).
Your goal
You bring nginx from an insecure default to a modern TLS configuration. The config lives in /etc/nginx/sites-enabled/lab-default.conf; after changes verify with nginx -t and reload with systemctl reload nginx.
Exercises
1. Generate a self-signed certificate
Concept: self-signed certificate. A certificate needs a private key (secret) and a public certificate (
server.crt). Here you create both, self-signed. A helper script handles the complexopensslsyntax and places the files in/etc/ssl/lab/(valid 365 days).sudo /usr/local/bin/lab-gen-cert.shInspect the script first with
bash -x /usr/local/bin/lab-gen-cert.shto understand whatopenssl req -x509does.Check:
openssl x509 -in /etc/ssl/lab/server.crt -noout -subjectshows a line withCN = ....2. Point nginx at the new certificate
Concept: effective configuration. A good certificate is useless if nginx keeps loading the old default path (
snakeoil). Only thessl_certificatedirectives make your cert effective.Set in
/etc/nginx/sites-enabled/lab-default.conf(sudo nano ...):ssl_certificate /etc/ssl/lab/server.crt; ssl_certificate_key /etc/ssl/lab/server.key;Then test and reload:
sudo nginx -t && sudo systemctl reload nginxCheck: the config contains
ssl_certificate /etc/ssl/lab/server.crt.3. Switch off legacy TLS versions
Concept: TLS protocol versions.
TLSv1.0andTLSv1.1are considered broken — modern browsers warn or refuse the connection. Allow only the current versions.Set in the nginx config:
ssl_protocols TLSv1.2 TLSv1.3;Then
sudo nginx -t && sudo systemctl reload nginx.Check:
nginx -Tshowsssl_protocols TLSv1.2 TLSv1.3.4. Set a strong cipher suite
Concept: cipher suite. The cipher suite defines the concrete encryption algorithms. Weak ones like
RC4or3DESare exploitable. Safe areECDHEsuites withGCM(they provide forward secrecy).Set in the nginx config:
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers on;Then
sudo nginx -t && sudo systemctl reload nginx.Check: the config contains an
ssl_ciphersline withECDHE+GCM(noRC4/3DES/MEDIUM).5. Set the HSTS header
Concept: HSTS. The
Strict-Transport-Securityheader tells the browser: "for this site use HTTPS only, never HTTP again." This prevents downgrade attacks (e.g. SSL strip).Set in the server block of the config:
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains" always;Important: in the server block (not the location block) — otherwise inherited headers get overwritten.
Then reload and check:
curl -sIk https://localhost/ | grep -i strict.Check:
curl -sIk https://localhost/returns aStrict-Transport-Security: max-age=...header.6. Set the Content-Security-Policy header
Concept: CSP. The
Content-Security-Policylimits which sources the browser may load content from.default-src 'self'means: only from your own domain. This is defence-in-depth against XSS — even injected JavaScript will not run.Set in the server block:
add_header Content-Security-Policy "default-src 'self'" always;Then reload and check:
curl -sIk https://localhost/ | grep -i content-security.Check:
curl -sIk https://localhost/returnsContent-Security-Policy: default-src 'self'.7. Resolve Lynis TLS suggestions
Concept: Lynis audit. Lynis is a security audit tool that scans a system and provides a hardening score plus concrete suggestions. If your previous 6 steps are clean, the TLS-related Lynis hints (NETW-3032, HTTP-6624) should be covered.
Run the audit and address any remaining TLS suggestions:
sudo lynis audit system --quietThe suggestions are at the end of the output. If steps 1–6 are done, this check should pass automatically.
Check: the Lynis hardening score has improved sufficiently over the baseline.
Now practice it yourself
Reading is good – doing is better. Start this course on a real Linux VM, right in your browser. A free account is all it takes.
Start for freeLab content under CC BY 4.0 – free to use with attribution (© TechLogia).
