Bài 7: SSL/TLS và HTTPS trong NGINX
Bài học về SSL/TLS và HTTPS trong Nginx - cấu hình SSL certificates với Let's Encrypt, HTTP to HTTPS redirect, SSL protocols và ciphers optimization, HSTS, OCSP Stapling và HTTP/2. Hướng dẫn bảo mật kết nối, tối ưu performance và đạt A+ rating trên SSL Labs.
1. Cấu hình SSL Certificate (Let's Encrypt)
1.1. Giới thiệu SSL/TLS
SSL (Secure Sockets Layer) / TLS (Transport Layer Security) là protocols để encrypt communication giữa client và server.
Tại sao cần HTTPS:
- Bảo mật dữ liệu (encryption)
- Xác thực server (authentication)
- Toàn vẹn dữ liệu (integrity)
- SEO benefits (Google ranking)
- Browser trust (không có warning)
- Required cho HTTP/2
- Required cho PWA (Progressive Web Apps)
Certificate Authority (CA):
- Let's Encrypt - Free, automated
- DigiCert, Comodo, GlobalSign - Commercial
- Self-signed - Development only
1.2. Cài đặt Certbot (Let's Encrypt Client)
Ubuntu/Debian:
# Update package list
sudo apt update
# Install Certbot
sudo apt install certbot python3-certbot-nginx -y
# Verify installation
certbot --version
CentOS/RHEL:
# Install EPEL repository
sudo yum install epel-release -y
# Install Certbot
sudo yum install certbot python3-certbot-nginx -y
# Or for CentOS 8+
sudo dnf install certbot python3-certbot-nginx -y
macOS:
# Using Homebrew
brew install certbot
# Nginx plugin
brew install certbot-nginx
1.3. Obtain SSL Certificate - Automatic Method
Method 1: Certbot automatic configuration
# Certbot sẽ tự động configure Nginx
sudo certbot --nginx -d example.com -d www.example.com
# Follow prompts:
# - Enter email address
# - Agree to terms
# - Choose: redirect HTTP to HTTPS (recommended)
Certbot sẽ:
- Verify domain ownership
- Obtain certificate
- Automatically configure Nginx
- Setup auto-renewal
Kiểm tra certificate:
# List certificates
sudo certbot certificates
# Output:
# Certificate Name: example.com
# Domains: example.com www.example.com
# Expiry Date: 2024-03-01 10:30:00+00:00 (VALID: 89 days)
# Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
# Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem
1.4. Obtain SSL Certificate - Manual Method
Method 2: Certbot certonly (manual configuration)
# Obtain certificate without auto-config
sudo certbot certonly --nginx -d example.com -d www.example.com
# Or using webroot
sudo certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com
# Or using standalone (stops Nginx temporarily)
sudo systemctl stop nginx
sudo certbot certonly --standalone -d example.com -d www.example.com
sudo systemctl start nginx
Manual Nginx configuration:
server {
listen 80;
server_name example.com www.example.com;
# ACME challenge location
location /.well-known/acme-challenge/ {
root /var/www/html;
}
# Redirect to HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL certificate files
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL configuration (will add more later)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
1.5. Certificate Renewal
Let's Encrypt certificates expire sau 90 ngày. Certbot setup automatic renewal.
Test renewal:
# Dry run (test without actually renewing)
sudo certbot renew --dry-run
Manual renewal:
# Renew all certificates
sudo certbot renew
# Renew specific certificate
sudo certbot renew --cert-name example.com
# Renew và reload Nginx
sudo certbot renew --deploy-hook "systemctl reload nginx"
Automatic renewal (systemd timer):
# Check if timer is active
sudo systemctl status certbot.timer
# Enable timer
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
# List timers
sudo systemctl list-timers | grep certbot
Renewal hook script:
# Create renewal hook
sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/bash
# Reload Nginx after certificate renewal
systemctl reload nginx
# Make executable
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
1.6. Wildcard Certificates
# Wildcard certificate (requires DNS challenge)
sudo certbot certonly --manual --preferred-challenges dns \
-d example.com -d *.example.com
# Follow instructions to add DNS TXT record
# _acme-challenge.example.com TXT "generated-token"
# Verify DNS propagation
dig _acme-challenge.example.com TXT
# Continue with certbot
1.7. Multiple Domains
# Multiple domains on one certificate
sudo certbot --nginx \
-d example.com -d www.example.com \
-d blog.example.com -d shop.example.com
# Or separate certificates
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot --nginx -d blog.example.com
sudo certbot --nginx -d shop.example.com
2. HTTP to HTTPS Redirect
2.1. Simple Redirect
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Redirect all HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Your site configuration
root /var/www/html;
index index.html;
}
2.2. Redirect với ACME Challenge
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Allow ACME challenge
location /.well-known/acme-challenge/ {
root /var/www/html;
allow all;
}
# Redirect everything else to HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
2.3. Redirect www to non-www (HTTPS)
# Redirect www to non-www
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 301 https://example.com$request_uri;
}
# Main site
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Site config...
}
2.4. Complete Redirect Configuration
# HTTP - redirect to HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://example.com$request_uri;
}
}
# HTTPS www - redirect to non-www
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 301 https://example.com$request_uri;
}
# Main HTTPS site
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
# SSL certificates
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# Site content
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
3. SSL Protocols và Ciphers
3.1. SSL/TLS Protocols
Available protocols:
- SSLv2 - Deprecated, insecure ❌
- SSLv3 - Deprecated, insecure ❌
- TLSv1.0 - Deprecated, should avoid ⚠️
- TLSv1.1 - Deprecated, should avoid ⚠️
- TLSv1.2 - Secure, widely supported ✅
- TLSv1.3 - Most secure, modern ✅
Recommended configuration:
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Use only TLS 1.2 and 1.3
ssl_protocols TLSv1.2 TLSv1.3;
# Prefer server ciphers (for TLS 1.2)
ssl_prefer_server_ciphers off; # TLS 1.3 handles this automatically
}
Backward compatibility (if needed):
# Support older clients (not recommended for production)
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
Modern configuration (TLS 1.3 only):
# Most secure, but may break older clients
ssl_protocols TLSv1.3;
3.2. SSL Ciphers
Ciphers xác định encryption algorithms được sử dụng.
Mozilla Modern Configuration (Recommended):
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Protocols
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
# Ciphers (TLS 1.3 handles automatically)
}
Mozilla Intermediate Configuration (Balanced):
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Protocols
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
# Ciphers for TLS 1.2
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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
}
Complete SSL Configuration:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
# Certificate files
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Protocols
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
# Ciphers
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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
# Session cache
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# DH parameters
ssl_dhparam /etc/nginx/dhparam.pem;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}
3.3. Generate DH Parameters
Diffie-Hellman parameters tăng cường security.
# Generate 2048-bit DH parameters (takes a few minutes)
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048
# Or 4096-bit (takes longer, more secure)
sudo openssl dhparam -out /etc/nginx/dhparam.pem 4096
# Set permissions
sudo chmod 644 /etc/nginx/dhparam.pem
Add to Nginx config:
server {
listen 443 ssl http2;
ssl_dhparam /etc/nginx/dhparam.pem;
# Other SSL config...
}
3.4. SSL Session Configuration
http {
# SSL session cache (shared across workers)
ssl_session_cache shared:SSL:10m; # 10MB = ~40,000 sessions
# Session timeout
ssl_session_timeout 10m; # 10 minutes
# Disable session tickets (for perfect forward secrecy)
ssl_session_tickets off;
server {
listen 443 ssl http2;
# Inherit from http context
}
}
Session cache sizes:
1MB = ~4,000 sessions
10MB = ~40,000 sessions
100MB = ~400,000 sessions
4. HSTS (HTTP Strict Transport Security)
HSTS instructs browsers to always use HTTPS.
4.1. Basic HSTS
server {
listen 443 ssl http2;
server_name example.com;
# HSTS header
add_header Strict-Transport-Security "max-age=31536000" always;
# Other config...
}
max-age values:
# Testing - 1 hour
add_header Strict-Transport-Security "max-age=3600" always;
# Short term - 1 week
add_header Strict-Transport-Security "max-age=604800" always;
# Recommended - 1 year
add_header Strict-Transport-Security "max-age=31536000" always;
# Maximum - 2 years
add_header Strict-Transport-Security "max-age=63072000" always;
4.2. HSTS với includeSubDomains
server {
listen 443 ssl http2;
server_name example.com;
# Apply HSTS to all subdomains
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
CẢNH BÁO: includeSubDomains affects ALL subdomains. Make sure all subdomains support HTTPS.
4.3. HSTS Preload
server {
listen 443 ssl http2;
server_name example.com;
# HSTS with preload directive
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}
Submit to HSTS Preload List:
- Visit https://hstspreload.org/
- Enter your domain
- Check requirements:
- Serve valid certificate
- Redirect HTTP to HTTPS
- Serve HSTS header on base domain
- max-age >= 31536000 (1 year)
- includeSubDomains directive
- preload directive
4.4. Complete HSTS Configuration
# HTTP server - redirect to HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://example.com$request_uri;
}
}
# HTTPS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL config
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# HSTS header
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Security headers
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 Referrer-Policy "no-referrer-when-downgrade" always;
# Content
root /var/www/html;
index index.html;
}
5. OCSP Stapling
OCSP Stapling improves SSL/TLS handshake performance và privacy.
5.1. What is OCSP Stapling?
Without OCSP Stapling:
Client → Server: SSL handshake
Client → CA: Is certificate valid?
CA → Client: Yes, valid
Client → Server: Continue
With OCSP Stapling:
Server → CA: Is my certificate valid? (cached)
Client → Server: SSL handshake
Server → Client: Here's my certificate + OCSP response
Client: Certificate valid! (no extra request to CA)
5.2. Enable OCSP Stapling
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Enable OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
# Trusted certificate for verification
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# DNS resolvers for OCSP
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}
5.3. Verify OCSP Stapling
# Test OCSP stapling
echo QUIT | openssl s_client -connect example.com:443 -status 2> /dev/null | grep -A 17 'OCSP response:'
# Expected output:
# OCSP response:
# ======================================
# OCSP Response Status: successful (0x0)
# Response Type: Basic OCSP Response
# ...
# Cert Status: good
Online test:
# Using SSL Labs
# Visit: https://www.ssllabs.com/ssltest/analyze.html?d=example.com
5.4. Complete OCSP Configuration
http {
# Global resolver (can be overridden per server)
resolver 8.8.8.8 8.8.4.4 1.1.1.1 valid=300s;
resolver_timeout 5s;
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
# SSL certificates
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# SSL protocols
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# Session settings
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Other security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
root /var/www/html;
index index.html;
}
}
6. HTTP/2 Configuration
HTTP/2 cải thiện performance significantly với multiplexing, server push, và header compression.
6.1. Enable HTTP/2
server {
# Enable HTTP/2 with http2 parameter
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# HTTP/2 requires TLS 1.2+
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/html;
}
Check if HTTP/2 is enabled:
# Test with curl
curl -I --http2 https://example.com
# Look for:
# HTTP/2 200
# Or check browser DevTools
# Network tab → Protocol column should show "h2"
6.2. HTTP/2 Push
Server Push allows server to send resources before client requests them.
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/html;
location / {
# Push CSS and JS when HTML is requested
http2_push /css/style.css;
http2_push /js/app.js;
try_files $uri $uri/ =404;
}
location = /index.html {
# Push specific resources for homepage
http2_push /css/style.css;
http2_push /css/bootstrap.css;
http2_push /js/app.js;
http2_push /js/jquery.js;
http2_push /images/logo.png;
}
}
Conditional push:
map $http_cookie $css_push {
default "/css/style.css";
~*visited ""; # Don't push if user visited before
}
server {
listen 443 ssl http2;
location / {
http2_push $css_push;
}
}
CẢNH BÁO: HTTP/2 Push có thể làm giảm performance nếu dùng sai. Chỉ push critical resources.
6.3. HTTP/2 Parameters
http {
# HTTP/2 settings
http2_max_field_size 16k; # Max header field size
http2_max_header_size 32k; # Max header size
http2_max_requests 1000; # Max requests per connection
http2_recv_timeout 30s; # Timeout for client
server {
listen 443 ssl http2;
server_name example.com;
# Server inherits http2 settings
}
}
6.4. Complete HTTP/2 Configuration
http {
# HTTP/2 parameters
http2_max_field_size 16k;
http2_max_header_size 32k;
http2_max_requests 1000;
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# Session cache
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# 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 X-XSS-Protection "1; mode=block" always;
root /var/www/html;
index index.html;
location / {
# HTTP/2 Server Push for critical resources
http2_push /css/style.css;
http2_push /js/app.js;
try_files $uri $uri/ =404;
}
# Cache static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
}
}
7. Complete Production-Ready SSL Configuration
7.1. Optimal SSL/TLS Setup
# /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# Basic settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 20M;
# Hide Nginx version
server_tokens off;
# SSL session cache
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# OCSP settings
resolver 8.8.8.8 8.8.4.4 1.1.1.1 valid=300s;
resolver_timeout 5s;
# Gzip compression
gzip on;
gzip_vary on;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss
application/rss+xml font/truetype font/opentype
application/vnd.ms-fontobject image/svg+xml;
# HTTP/2 settings
http2_max_field_size 16k;
http2_max_header_size 32k;
# Include server configs
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
7.2. Site Configuration
# /etc/nginx/sites-available/example.com
# HTTP - redirect to HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# ACME challenge
location /.well-known/acme-challenge/ {
root /var/www/html;
allow all;
}
# Redirect to HTTPS
location / {
return 301 https://example.com$request_uri;
}
}
# HTTPS www - redirect to non-www
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.example.com;
# SSL certificates
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# Redirect to non-www
return 301 https://example.com$request_uri;
}
# Main HTTPS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
# Document root
root /var/www/example.com/public;
index index.html index.htm;
# Logging
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
# SSL certificates
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# SSL protocols and ciphers
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
# DH parameters
ssl_dhparam /etc/nginx/dhparam.pem;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
# 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 X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; style-src 'self' 'unsafe-inline' https:;" always;
# Main location
location / {
try_files $uri $uri/ =404;
# HTTP/2 Push
http2_push /css/style.css;
http2_push /js/app.js;
}
# Static assets
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.(css|js)$ {
expires 1M;
add_header Cache-Control "public";
access_log off;
}
location ~* \.(woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public";
add_header Access-Control-Allow-Origin "*";
access_log off;
}
# Deny access to hidden files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Error pages
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
}
7.3. Enable Site
# Create symlink
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Reload Nginx
sudo systemctl reload nginx
8. Testing và Optimization
8.1. SSL Labs Test
# Visit SSL Labs
# https://www.ssllabs.com/ssltest/analyze.html?d=example.com
# Target: A+ rating
Checklist cho A+ rating:
- ✅ TLS 1.2 và 1.3 enabled
- ✅ Strong ciphers
- ✅ Certificate valid và trusted
- ✅ HSTS enabled (với preload)
- ✅ OCSP Stapling working
- ✅ No SSL/TLS vulnerabilities
8.2. Test Commands
# Test SSL connection
openssl s_client -connect example.com:443 -tls1_2
# Test TLS 1.3
openssl s_client -connect example.com:443 -tls1_3
# Test certificate
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# Test OCSP stapling
echo QUIT | openssl s_client -connect example.com:443 -status 2> /dev/null | grep -A 17 'OCSP response:'
# Test HTTP/2
curl -I --http2 https://example.com
# Test with specific cipher
openssl s_client -connect example.com:443 -cipher ECDHE-RSA-AES128-GCM-SHA256
8.3. Performance Testing
# Test SSL handshake time
time openssl s_client -connect example.com:443 </dev/null
# Benchmark with ab
ab -n 1000 -c 10 https://example.com/
# Test with h2load (HTTP/2)
h2load -n 1000 -c 10 https://example.com/
8.4. Security Headers Check
# Check all security headers
curl -I https://example.com
# Should include:
# Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
# X-Frame-Options: SAMEORIGIN
# X-Content-Type-Options: nosniff
# X-XSS-Protection: 1; mode=block
# Referrer-Policy: no-referrer-when-downgrade
Online tools:
- https://securityheaders.com
- https://observatory.mozilla.org
9. Troubleshooting
9.1. Certificate Errors
Problem: Certificate not trusted
# Check certificate chain
openssl s_client -connect example.com:443 -showcerts
# Verify certificate files
sudo ls -la /etc/letsencrypt/live/example.com/
# Should have:
# cert.pem (certificate)
# chain.pem (intermediate certificates)
# fullchain.pem (cert + chain)
# privkey.pem (private key)
Fix:
# Use fullchain.pem, not cert.pem
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
9.2. Mixed Content Warnings
Problem: Site loads but shows "Not Secure"
Cause: Page served over HTTPS but loads HTTP resources
Check:
# View source và tìm http:// (not https://)
curl https://example.com | grep 'http://'
Fix:
<!-- Bad -->
<script src="http://example.com/js/app.js"></script>
<img src="http://example.com/image.jpg">
<!-- Good - protocol-relative -->
<script src="//example.com/js/app.js"></script>
<img src="//example.com/image.jpg">
<!-- Better - HTTPS -->
<script src="https://example.com/js/app.js"></script>
<img src="https://example.com/image.jpg">
9.3. OCSP Stapling Not Working
Problem: OCSP response not included
# Test OCSP
echo QUIT | openssl s_client -connect example.com:443 -status 2> /dev/null | grep 'OCSP response:'
# If no output, check:
Fix:
server {
# Ensure these are set
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# Add resolvers
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}
# Test manually
sudo nginx -t
sudo systemctl reload nginx
# Wait a few seconds then test again
9.4. HTTP/2 Not Working
Problem: Connection uses HTTP/1.1 instead of HTTP/2
Check:
# Test HTTP/2
curl -I --http2 https://example.com
# Should show: HTTP/2 200
# If shows: HTTP/1.1 200
Fix:
# Ensure http2 parameter present
listen 443 ssl http2; # Not just: listen 443 ssl;
# Restart Nginx
sudo systemctl restart nginx
9.5. Certificate Renewal Fails
Problem: Certbot renewal fails
# Check renewal
sudo certbot renew --dry-run
# Common errors:
Fix 1: Port 80 not accessible
# Ensure port 80 open
sudo ufw allow 80
sudo firewall-cmd --permanent --add-service=http
Fix 2: Webroot not accessible
server {
listen 80;
# Ensure this location exists
location /.well-known/acme-challenge/ {
root /var/www/html; # Verify path is correct
allow all;
}
}
Fix 3: Manual renewal
# Stop Nginx
sudo systemctl stop nginx
# Use standalone
sudo certbot certonly --standalone -d example.com
# Start Nginx
sudo systemctl start nginx
10. Bài tập Thực hành
Bài tập 1: Setup HTTPS với Let's Encrypt
- Install Certbot
- Obtain certificate cho domain
- Configure Nginx với HTTPS
- Test certificate
Bài tập 2: Implement HTTP to HTTPS Redirect
- Setup HTTP server (port 80)
- Setup HTTPS server (port 443)
- Configure redirect từ HTTP → HTTPS
- Test redirect
Bài tập 3: Enable HSTS
- Add HSTS header
- Test với browser
- Check HSTS preload requirements
- (Optional) Submit to HSTS preload list
Bài tập 4: Configure OCSP Stapling
- Enable OCSP stapling
- Configure resolvers
- Test OCSP response
- Verify với SSL Labs
Bài tập 5: Enable HTTP/2
- Add http2 parameter to listen directive
- Test HTTP/2 connection
- Implement HTTP/2 push
- Benchmark HTTP/1.1 vs HTTP/2
Bài tập 6: Achieve A+ Rating
- Configure optimal SSL/TLS settings
- Enable all security features
- Test với SSL Labs
- Fix any issues để đạt A+ rating
11. Best Practices
11.1. Security
# Use strong protocols
ssl_protocols TLSv1.2 TLSv1.3;
# Strong ciphers
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# Disable session tickets
ssl_session_tickets off;
# Enable OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
11.2. Performance
# Session cache
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# HTTP/2
listen 443 ssl http2;
# Compression
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# Cache static assets
location ~* \.(jpg|png|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
11.3. Maintenance
# Regular certificate renewal
sudo certbot renew
# Check certificate expiry
sudo certbot certificates
# Monitor logs
sudo tail -f /var/log/letsencrypt/letsencrypt.log
# Backup certificates
sudo tar -czf letsencrypt-backup.tar.gz /etc/letsencrypt/
11.4. Monitoring
# Monitor SSL Labs rating
# Setup automated checks
# Monitor certificate expiry
# Alert 30 days before expiry
# Monitor OCSP stapling
# Check periodically
# Check security headers
# Automated testing
Tổng kết
Trong bài này, bạn đã học:
- ✅ Setup SSL certificates với Let's Encrypt
- ✅ HTTP to HTTPS redirects
- ✅ SSL protocols và ciphers optimization
- ✅ HSTS configuration và preload
- ✅ OCSP Stapling cho better performance
- ✅ HTTP/2 configuration và optimization
- ✅ Security headers và best practices
- ✅ Testing và troubleshooting
Bài tiếp theo: Chúng ta sẽ tìm hiểu về Performance Tuning - worker processes, connections, buffers, timeouts, compression và caching optimization để maximize Nginx performance.