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 updateInstall Certbot
sudo apt install certbot python3-certbot-nginx -y
Verify installation
certbot --version
CentOS/RHEL:
# Install EPEL repository sudo yum install epel-release -yInstall 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 certbotNginx 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.comFollow 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 certificatesOutput:
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.comOr 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 renewRenew 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.timerEnable 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.comFollow 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.comOr 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 2048Or 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 LabsVisit: 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.comLook 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 clientserver { 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.confuser 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.comHTTP - 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 Labshttps://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_2Test 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/nullBenchmark 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.comShould 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:
9. Troubleshooting
9.1. Certificate Errors
Problem: Certificate not trusted
# Check certificate chain openssl s_client -connect example.com:443 -showcertsVerify 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 nginxWait 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.comShould 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-runCommon 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 nginxUse 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 renewCheck 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 ratingSetup 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.