Chuyển đến nội dung chính

第7課:Nginx 中的 SSL/TLS 與 HTTPS

Nginx 中的 SSL/TLS 與 HTTPS——使用 Let's Encrypt 設定 SSL 憑證、 HTTP 轉 HTTPS 重新導向、SSL 協定與加密套件最佳化、 HSTS、OCSP Stapling、HTTP/2。 保護連線、效能最佳化、取得 SSL Labs A+ 評分的完整指南。

🔒 DevSecOps — 第7課 第7課:Nginx 中的 SSL/TLS 與 HTTPS Nginx 從基礎到進階 第3部分:安全性與效能 xdev.asia

1. 設定 SSL 憑證(Let's Encrypt)

1.1. SSL/TLS 概述

SSL(Secure Sockets Layer)/ TLS(Transport Layer Security)是加密用戶端與伺服器之間通訊的協定。

為什麼需要 HTTPS:

  • 資料安全性(加密)
  • 伺服器驗證
  • 資料完整性
  • SEO 優勢(Google 排名)
  • 瀏覽器信任(無警告)
  • HTTP/2 的必要條件
  • PWA(漸進式網頁應用程式)的必要條件

憑證授權機構(CA):

  • Let's Encrypt — 免費、自動化
  • DigiCert、Comodo、GlobalSign — 商業服務
  • 自我簽署 — 僅用於開發環境

1.2. 安裝 Certbot(Let's Encrypt 用戶端)

Ubuntu/Debian:

# 更新套件清單
sudo apt update

安裝 Certbot

sudo apt install certbot python3-certbot-nginx -y

確認安裝

certbot --version

CentOS/RHEL:

# 安裝 EPEL 儲存庫
sudo yum install epel-release -y

安裝 Certbot

sudo yum install certbot python3-certbot-nginx -y

CentOS 8+

sudo dnf install certbot python3-certbot-nginx -y

1.3. 取得 SSL 憑證 — 自動方式

方法一:Certbot 自動設定

# Certbot 自動設定 Nginx
sudo certbot --nginx -d example.com -d www.example.com

依提示操作:

- 輸入電子郵件地址

- 同意服務條款

- 選擇:將 HTTP 重新導向至 HTTPS(建議)

Certbot 的處理流程:

  1. 驗證網域所有權
  2. 取得憑證
  3. 自動設定 Nginx
  4. 設定自動更新

確認憑證:

# 列出憑證
sudo certbot certificates

輸出範例:

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. 取得 SSL 憑證 — 手動方式

方法二:Certbot certonly(手動設定)

# 取得憑證但不自動設定
sudo certbot certonly --nginx -d example.com -d www.example.com

或使用 webroot

sudo certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com

或使用 standalone(暫時停止 Nginx)

sudo systemctl stop nginx sudo certbot certonly --standalone -d example.com -d www.example.com sudo systemctl start nginx

手動 Nginx 設定:

server {
listen 80;
server_name example.com www.example.com;

# ACME challenge 位置
location /.well-known/acme-challenge/ {
    root /var/www/html;
}

# 重新導向至 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 憑證檔案
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

# SSL 設定
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. 憑證更新

Let's Encrypt 憑證 90 天後到期。Certbot 會設定自動更新。

測試更新:

# 乾跑(不實際更新)
sudo certbot renew --dry-run

手動更新:

# 更新所有憑證
sudo certbot renew

更新特定憑證

sudo certbot renew --cert-name example.com

更新後重新載入 Nginx

sudo certbot renew --deploy-hook "systemctl reload nginx"

自動更新(systemd 計時器):

# 確認計時器已啟用
sudo systemctl status certbot.timer

啟用計時器

sudo systemctl enable certbot.timer sudo systemctl start certbot.timer

1.6. 萬用字元憑證

sudo certbot certonly --manual --preferred-challenges dns 
-d example.com -d *.example.com

依指示新增 DNS TXT 記錄

_acme-challenge.example.com TXT "generated-token"

確認 DNS 傳播

dig _acme-challenge.example.com TXT

1.7. 多個網域

# 一張憑證包含多個網域
sudo certbot --nginx 
-d example.com -d www.example.com
-d blog.example.com -d shop.example.com

2. HTTP 轉 HTTPS 重新導向

2.1. 簡單重新導向

server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;

# 將所有 HTTP 重新導向至 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;

root /var/www/html;
index index.html;

}

2.2. 含 ACME Challenge 的重新導向

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;
}

# 其餘全部重新導向至 HTTPS
location / {
    return 301 https://$server_name$request_uri;
}

}

2.3. www 轉非 www 重新導向(HTTPS)

# 將 www 重新導向至非 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;

}

主要網站

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;

# 網站設定...

}

2.4. 完整重新導向設定

# HTTP — 重新導向至 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 — 重新導向至非 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;

}

主要 HTTPS 伺服器

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;

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;

root /var/www/html;
index index.html;

location / {
    try_files $uri $uri/ =404;
}

}


3. SSL 協定與加密套件

3.1. SSL/TLS 協定

可用協定:

  • SSLv2 — 已棄用,不安全 ❌
  • SSLv3 — 已棄用,不安全 ❌
  • TLSv1.0 — 已棄用,應避免 ⚠️
  • TLSv1.1 — 已棄用,應避免 ⚠️
  • TLSv1.2 — 安全,廣泛支援 ✅
  • TLSv1.3 — 最安全,現代標準 ✅

建議設定:

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;

# 僅使用 TLS 1.2 與 1.3
ssl_protocols TLSv1.2 TLSv1.3;

# 優先使用伺服器加密套件(TLS 1.2)
ssl_prefer_server_ciphers off;  # TLS 1.3 自動處理

}

3.2. SSL 加密套件

加密套件決定使用的加密演算法。

Mozilla 中間設定(平衡):

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;

# 協定
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;

# 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;

}

完整 SSL 設定:

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;

# 協定
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;

# 工作階段快取
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;

# DH 參數
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. 產生 DH 參數

Diffie-Hellman 參數可強化安全性。

# 產生 2048 位元 DH 參數(需要數分鐘)
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048

或 4096 位元(耗時較長,更安全)

sudo openssl dhparam -out /etc/nginx/dhparam.pem 4096

設定權限

sudo chmod 644 /etc/nginx/dhparam.pem

3.4. SSL 工作階段設定

http {
# SSL 工作階段快取(跨工作程序共享)
ssl_session_cache shared:SSL:10m;  # 10MB = 約 40,000 個工作階段

# 工作階段逾時
ssl_session_timeout 10m;           # 10 分鐘

# 停用工作階段票證(完美前向保密)
ssl_session_tickets off;

}


4. HSTS(HTTP 嚴格傳輸安全)

HSTS 指示瀏覽器一律使用 HTTPS。

4.1. 基本 HSTS

server {
listen 443 ssl http2;
server_name example.com;

# HSTS 標頭
add_header Strict-Transport-Security "max-age=31536000" always;

}

max-age 值:

# 測試 — 1 小時
add_header Strict-Transport-Security "max-age=3600" always;

短期 — 1 週

add_header Strict-Transport-Security "max-age=604800" always;

建議 — 1 年

add_header Strict-Transport-Security "max-age=31536000" always;

最長 — 2 年

add_header Strict-Transport-Security "max-age=63072000" always;

4.2. 含 includeSubDomains 的 HSTS

server {
listen 443 ssl http2;
server_name example.com;

# 將 HSTS 套用至所有子網域
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

}

警告: includeSubDomains 會影響所有子網域。請確認所有子網域都支援 HTTPS。

4.3. HSTS Preload

server {
listen 443 ssl http2;
server_name example.com;

# 含 preload 指令的 HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

}

提交至 HSTS Preload 清單:

  1. 前往 https://hstspreload.org/
  2. 輸入您的網域
  3. 確認要求:
    • 提供有效憑證
    • 將 HTTP 重新導向至 HTTPS
    • 在根網域提供 HSTS 標頭
    • max-age ≥ 31536000(1 年)
    • includeSubDomains 指令
    • preload 指令

4.4. 完整 HSTS 設定

# HTTP — 重新導向至 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 { 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;
ssl_protocols TLSv1.2 TLSv1.3;

# HSTS 標頭
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;

root /var/www/html;
index index.html;

}


5. OCSP Stapling

OCSP Stapling 可提升 SSL/TLS 握手效能與隱私保護。

5.1. 什麼是 OCSP Stapling?

無 OCSP Stapling:

用戶端 → 伺服器:SSL 握手
用戶端 → CA:憑證是否有效?
CA → 用戶端:是的,有效
用戶端 → 伺服器:繼續

有 OCSP Stapling:

伺服器 → CA:我的憑證是否有效?(已快取)
用戶端 → 伺服器:SSL 握手
伺服器 → 用戶端:憑證 + OCSP 回應
用戶端:憑證有效!(無需額外請求 CA)

5.2. 啟用 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;

# 啟用 OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;

# 用於驗證的信任憑證
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

# OCSP 的 DNS 解析器
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

}

5.3. 確認 OCSP Stapling

# 測試 OCSP Stapling
echo QUIT | openssl s_client -connect example.com:443 -status 2> /dev/null | grep -A 17 'OCSP response:'

預期輸出:

OCSP response:

======================================

OCSP Response Status: successful (0x0)

Response Type: Basic OCSP Response

...

Cert Status: good

5.4. 完整 OCSP 設定

http {
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_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 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;
    
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;
    
    ssl_stapling on;
    ssl_stapling_verify on;
    
    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;
}

}


6. HTTP/2 設定

HTTP/2 透過多工處理、伺服器推送和標頭壓縮大幅提升效能。

6.1. 啟用 HTTP/2

server {
# 使用 http2 參數啟用 HTTP/2
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 需要 TLS 1.2 以上
ssl_protocols TLSv1.2 TLSv1.3;

root /var/www/html;

}

確認 HTTP/2 是否已啟用:

# 使用 curl 測試
curl -I --http2 https://example.com

確認出現:

HTTP/2 200

或在瀏覽器開發者工具中確認

網路分頁 → 通訊協定欄位應顯示「h2」

6.2. HTTP/2 推送

伺服器推送允許伺服器在用戶端請求前主動發送資源。

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 / {
    # 請求 HTML 時推送 CSS 和 JS
    http2_push /css/style.css;
    http2_push /js/app.js;
    
    try_files $uri $uri/ =404;
}

}

警告: HTTP/2 推送若使用不當會降低效能。請僅推送關鍵資源。

6.3. HTTP/2 參數

http {
# HTTP/2 設定
http2_max_field_size 16k;        # 標頭欄位最大大小
http2_max_header_size 32k;       # 標頭最大大小
http2_max_requests 1000;          # 每個連線最大請求數
http2_recv_timeout 30s;           # 用戶端逾時
}

7. 正式環境完整 SSL 設定

7.1. 最佳 SSL/TLS 設定

# /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;

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;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 20M;

# 隱藏 Nginx 版本
server_tokens off;

# SSL 工作階段快取
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;

# OCSP 設定
resolver 8.8.8.8 8.8.4.4 1.1.1.1 valid=300s;
resolver_timeout 5s;

# Gzip 壓縮
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;

# HTTP/2 設定
http2_max_field_size 16k;
http2_max_header_size 32k;

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

}

7.2. 網站設定

# /etc/nginx/sites-available/example.com

HTTP — 重新導向至 HTTPS

server { listen 80; listen [::]:80; server_name example.com www.example.com;

location /.well-known/acme-challenge/ {
    root /var/www/html;
    allow all;
}

location / {
    return 301 https://example.com$request_uri;
}

}

HTTPS www — 重新導向至非 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;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

return 301 https://example.com$request_uri;

}

主要 HTTPS 伺服器

server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com;

root /var/www/example.com/public;
index index.html index.htm;

access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;

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 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;

ssl_dhparam /etc/nginx/dhparam.pem;

ssl_stapling on;
ssl_stapling_verify on;

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;

location / {
    try_files $uri $uri/ =404;
    
    http2_push /css/style.css;
    http2_push /js/app.js;
}

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 ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

}


8. 測試與最佳化

8.1. SSL Labs 測試

# 前往 SSL Labs

https://www.ssllabs.com/ssltest/analyze.html?d=example.com

目標:A+ 評分

A+ 評分清單:

  • ✅ TLS 1.2 與 1.3 已啟用
  • ✅ 強力加密套件
  • ✅ 憑證有效且受信任
  • ✅ HSTS 已啟用(含 preload)
  • ✅ OCSP Stapling 正常運作
  • ✅ 無 SSL/TLS 漏洞

8.2. 測試指令

# 測試 SSL 連線
openssl s_client -connect example.com:443 -tls1_2

測試 TLS 1.3

openssl s_client -connect example.com:443 -tls1_3

檢查憑證

echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

測試 OCSP Stapling

echo QUIT | openssl s_client -connect example.com:443 -status 2> /dev/null | grep -A 17 'OCSP response:'

測試 HTTP/2

curl -I --http2 https://example.com

8.3. 效能測試

# 測試 SSL 握手時間
time openssl s_client -connect example.com:443 </dev/null

使用 ab 進行基準測試

ab -n 1000 -c 10 https://example.com/

使用 h2load(HTTP/2)測試

h2load -n 1000 -c 10 https://example.com/

8.4. 安全標頭確認

# 確認所有安全標頭
curl -I https://example.com

應包含:

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


9. 疑難排解

9.1. 憑證錯誤

問題:憑證不受信任

# 檢查憑證鏈
openssl s_client -connect example.com:443 -showcerts

確認憑證檔案

sudo ls -la /etc/letsencrypt/live/example.com/

應包含:

cert.pem(憑證)

chain.pem(中繼憑證)

fullchain.pem(憑證 + 鏈)

privkey.pem(私鑰)

修正:

# 使用 fullchain.pem,而非 cert.pem
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

9.2. 混合內容警告

問題: 網站載入但顯示「不安全」

原因: 以 HTTPS 提供的頁面載入了 HTTP 資源

修正:

<!-- 錯誤寫法 -->
<script src="http://example.com/js/app.js"&gt;&lt;/script>

<!-- 正確寫法 — HTTPS --> <script src="https://example.com/js/app.js"&gt;&lt;/script>

9.3. OCSP Stapling 無法運作

修正:

server {
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;

}

9.4. HTTP/2 無法運作

修正:

# 確認 http2 參數存在
listen 443 ssl http2;  # 不能只寫 listen 443 ssl;

重新啟動 Nginx

sudo systemctl restart nginx

9.5. 憑證更新失敗

修正一:連接埠 80 無法存取

sudo ufw allow 80

修正二:手動更新

# 停止 Nginx
sudo systemctl stop nginx

使用 standalone

sudo certbot certonly --standalone -d example.com

啟動 Nginx

sudo systemctl start nginx


10. 練習題

練習1:使用 Let's Encrypt 設定 HTTPS

  1. 安裝 Certbot
  2. 取得網域憑證
  3. 在 Nginx 設定 HTTPS
  4. 測試憑證

練習2:實作 HTTP 轉 HTTPS 重新導向

  1. 設定 HTTP 伺服器(連接埠 80)
  2. 設定 HTTPS 伺服器(連接埠 443)
  3. 設定 HTTP → HTTPS 重新導向
  4. 測試重新導向

練習3:啟用 HSTS

  1. 新增 HSTS 標頭
  2. 在瀏覽器中測試
  3. 確認 HSTS preload 要求
  4. (選擇性)提交至 HSTS preload 清單

練習4:設定 OCSP Stapling

  1. 啟用 OCSP Stapling
  2. 設定解析器
  3. 測試 OCSP 回應
  4. 使用 SSL Labs 驗證

練習5:啟用 HTTP/2

  1. 在 listen 指令中新增 http2 參數
  2. 測試 HTTP/2 連線
  3. 實作 HTTP/2 推送
  4. 對比 HTTP/1.1 與 HTTP/2 的效能基準

練習6:取得 A+ 評分

  1. 設定最佳 SSL/TLS 配置
  2. 啟用所有安全功能
  3. 使用 SSL Labs 測試
  4. 修正任何問題以取得 A+ 評分

11. 最佳實踐

11.1. 安全性

# 使用強力協定
ssl_protocols TLSv1.2 TLSv1.3;

強力加密套件

ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;

停用工作階段票證

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;

11.2. 效能

# 工作階段快取
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

HTTP/2

listen 443 ssl http2;

壓縮

gzip on; gzip_types text/plain text/css application/json application/javascript;

靜態資源快取

location ~* .(jpg|png|css|js)$ { expires 1y; add_header Cache-Control "public, immutable"; }

11.3. 維護

# 定期更新憑證
sudo certbot renew

檢查憑證到期日

sudo certbot certificates

監控日誌

sudo tail -f /var/log/letsencrypt/letsencrypt.log

備份憑證

sudo tar -czf letsencrypt-backup.tar.gz /etc/letsencrypt/


總結

本課程學到的內容:

  • ✅ 使用 Let's Encrypt 設定 SSL 憑證
  • ✅ HTTP 轉 HTTPS 重新導向
  • ✅ SSL 協定與加密套件最佳化
  • ✅ HSTS 設定與 preload
  • ✅ OCSP Stapling 提升效能
  • ✅ HTTP/2 設定與最佳化
  • ✅ 安全標頭與最佳實踐
  • ✅ 測試與疑難排解

下一課: 效能調校——透過工作程序、連線、緩衝區、逾時、壓縮與快取最佳化,將 Nginx 效能發揮到極致。