💰 Docker 幫你 5 分鐘搞定免費 HTTPS!🤯 Let's Encrypt + Certbot 實戰手冊

更新 發佈閱讀 18 分鐘

許多開發者第一次把專案部署到正式環境時,都會遇到同一個問題: 我想讓網站變成 HTTPS,要怎麼做?

好消息是:只要你使用 Docker + Nginx,搭配 Let’s Encrypt + Certbot,其實很快就能讓服務安全上線,而且憑證還會「自動更新」,再也不用擔心過期。

本文我會用最簡單、最實用的方式示範整個流程,並在關鍵環節搭配圖示,幫助你更快理解架構。

🎯 目標架構

• 使用 Docker Compose 管理所有服務

• 一個簡單 Frontend(HTML)

• 一個簡單 Backend(API 回傳文字)

• DuckDNS 提供免費子網域

• Nginx 當反向代理

• Let’s Encrypt + Certbot 簽發與自動續期 HTTPS


🧱 全體架構一頁看懂

raw-image


• Nginx 負責對外提供 HTTP/HTTPS 入口

• Certbot 需要使用 .well-known/acme-challenge 來驗證你的網域

• 憑證產生後,直接放在共用的 volume

• Nginx 就能使用它來啟動 HTTPS


步驟一: 基礎的前後端容器架構

1-1 專案架構

https-demo/
├── backend/ # Flask API 後端
├── frontend/ # 靜態網頁前端
├── nginx/ # Nginx 反向代理
├── certbot/ # SSL 憑證存放
└── compose.yml # Docker Compose 配置

1-2 後端服務

建立 backend/app.py:

from flask import Flask
app = Flask(__name__)

@app.route("/api")
def api():
return "ok my https success"

app.run(host="0.0.0.0", port=5000)

建立 backend/Dockerfile:

FROM python:3.11-slim
WORKDIR /app
RUN pip install flask
COPY app.py .
CMD ["python", "app.py"]

1-3 前端服務

建立 frontend/index.html:

<!DOCTYPE html>
<html>
<body>
<h1>HTTPS Demo</h1>
<p id="result">Loading...</p>

<script>
fetch('/api')
.then(res => res.text())
.then(text => {
document.getElementById('result').innerText = text
})
.catch(() => {
document.getElementById('result').innerText = 'API error'
})
</script>
</body>
</html>

1-4 Nginx 配置(HTTP 版)

建立 nginx/default.conf:

server {
listen 80;
server_name localhost;

# Let's Encrypt 憑證驗證路徑(申請憑證時必需)
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}

location / {
root /usr/share/nginx/html;
index index.html;
}

location /api {
proxy_pass http://backend:5000;
}
}

建立 nginx/Dockerfile:

FROM nginx:alpine

# 複製 Nginx 設定檔
COPY nginx/default.conf /etc/nginx/conf.d/default.conf

# 複製前端靜態檔案到容器內(避免權限問題)
COPY frontend/ /usr/share/nginx/html/

# 設定正確的權限
RUN chmod -R 755 /usr/share/nginx/html && \
chown -R nginx:nginx /usr/share/nginx/html

1-5 Docker Compose 配置

建立 compose.yml:

services:
backend:
build: ./backend

nginx:
build:
context: .
dockerfile: nginx/Dockerfile
ports:
- "8888:80" # HTTP port
depends_on:
- backend

1-6 啟動服務

# 建置並啟動
docker compose up -d

# 查看狀態
docker compose ps

# 開啟瀏覽器訪問 http://your-server-ip:8888,應該看到前端頁面。
curl http://localhost:8888


步驟二: DuckDNS 提供免費子網域

通常我們將網站架設好之後, 預設都會用「http://201.xx.xx.xx」的方式來存取我們的網站, 但這樣就把我們家的ip暴露到外部了, 對於安全性來講少了一層防護, 因此我們可以藉由DNS網域的方式來避免IP直接暴露, 而這篇主要是為了教學使用免費的 DuckDNS 來示範, 更安全的網站可以使用付費的DNS服務, 功能會更多也更安全。

2-1 註冊並設定

• 前往 https://www.duckdns.org/

• 使用 GitHub/Google 帳號登入

• 建立一個子域名,例如:myapp.duckdns.org

• 在 current ip 欄位填入您的伺服器公開 IP

• 點擊 update ip 按鈕

2-2  更新 Nginx 配置

server {
listen 80;
server_name myapp.duckdns.org; # 改成你的域名

# Let's Encrypt 憑證驗證路徑(申請憑證時必需)
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}

location / {
root /usr/share/nginx/html;
index index.html;
}

location /api {
proxy_pass http://backend:5000;
}
}

2-3 重啟服務

docker compose build nginx
docker compose up -d

# 測試訪問 http://myapp.duckdns.org:8888

步驟三:啟用 HTTPS(Let's Encrypt + Certbot)

3-1 HTTPS 運作原理

raw-image

Let's Encrypt:免費的憑證授權機構(CA),提供 90 天有效期的 SSL 憑證。

Certbot:自動化工具,負責:

• 向 Let's Encrypt 申請憑證

• 驗證您擁有該域名

• 自動續期憑證(每 90 天)

3-2 憑證申請流程

raw-image


重要觀念:

• HTTP (Port 80/8888) 的作用:Let's Encrypt 必須透過 HTTP 訪問 /.well-known/acme-challenge/ 來驗證域名所有權。

• 為什麼不能只用 HTTPS:在還沒有憑證之前,無法啟用 HTTPS,所以需要先用 HTTP 完成驗證。

• 驗證完成後:HTTP 可以設定為自動重定向到 HTTPS。

3-3 準備 Certbot 目錄

# 建立憑證存放目錄
mkdir -p certbot/conf certbot/www
chmod -R 755 certbot

3-4 更新 Docker Compose(加入 Certbot)

services:
backend:
build: ./backend

nginx:
build:
context: .
dockerfile: nginx/Dockerfile
ports:
- "8888:80" # HTTP(用於 Let's Encrypt 驗證 + 重定向)
- "8889:443" # HTTPS(加密連線)
volumes:
# 掛載憑證目錄(憑證需要持久化儲存)
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
depends_on:
- backend

certbot:
image: certbot/certbot
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
# 每 12 小時檢查憑證是否需要續期
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

🎯 憑證自動續期機制

raw-image

3-5 一鍵申請憑證腳本

建立 init-letsencrypt.sh:

#!/bin/bash

# 設定您的域名和 Email
DOMAIN="myapp.duckdns.org"
EMAIL="your-email@example.com"

echo "### 啟動 Nginx(HTTP 模式)..."
docker compose up -d nginx

echo "### 等待 Nginx 啟動..."
sleep 5

echo "### 申請 Let's Encrypt 憑證..."
docker compose run --rm certbot certonly --webroot \
--webroot-path=/var/www/certbot \
--email $EMAIL \
--agree-tos \
--no-eff-email \
-d $DOMAIN

if [ $? -eq 0 ]; then
echo "### 憑證申請成功!"

echo "### 切換到 HTTPS 配置..."
# 修改 Dockerfile 使用 https.conf
sed -i 's/default.conf/https.conf/g' nginx/Dockerfile

echo "### 重新建置並啟動 Nginx(HTTPS 模式)..."
docker compose build nginx
docker compose up -d

echo "### 完成!"
echo "### HTTP: http://$DOMAIN:8888 (會自動重定向到 HTTPS)"
echo "### HTTPS: https://$DOMAIN:8889"
else
echo "### 憑證申請失敗,請檢查:"
echo "1. 域名是否正確指向此伺服器 IP"
echo "2. 防火牆是否開放 8888 port"
echo "3. Nginx 是否正常運行"
echo "4. 是否能夠訪問 http://$DOMAIN:8888"
fi

設定執行權限:

chmod +x init-letsencrypt.sh

3-6 更新 Nginx 配置啟用 HTTPS

憑證申請成功後,編輯 nginx/default.conf,將 HTTP server 的 location / 改為重定向,並新增 HTTPS server 區塊:

# HTTP 伺服器:處理 Let's Encrypt 驗證 + 重定向到 HTTPS
server {
listen 80;
server_name myapp.duckdns.org;

# Let's Encrypt 憑證驗證路徑(必須使用 HTTP
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}

# 其他所有請求都重定向到 HTTPS
location / {
return 301 https://$host:8889$request_uri;
}
}

# HTTPS 伺服器:處理加密連線
server {
listen 443 ssl http2;
server_name myapp.duckdns.org;

# SSL 憑證檔案位置
ssl_certificate /etc/letsencrypt/live/myapp.duckdns.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.duckdns.org/privkey.pem;

# SSL 安全性設定
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers HIGH:!aNULL:!MD5;

# 前端靜態檔案
location / {
root /usr/share/nginx/html;
index index.html;
}

# 後端 API 反向代理
location /api {
proxy_pass http://backend:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

⚠️ 3-7 暫時更改80 port 再執行 init-letsencrypt.sh 申請憑證 ⚠️

• Let's Encrypt 的 HTTP-01 驗證方式硬性規定使用 port 80

• 無法使用其他 port(如 8888)

• 但只在申請和續期憑證時才需要,平時可以使用自訂 port

建議做法:申請憑證時暫時停止佔用 80 port 的服務

# 1. 停止佔用 port 80 的服務(例如其他 Nginx)
sudo systemctl stop nginx
# 或
docker stop <container-name>

# 2. 修改 compose.yml 使用標準 port 80
ports:
- "80:80" # 申請憑證階段使用標準 port

# 3. 執行腳本申請憑證
./init-letsencrypt.sh

# 4. 憑證申請成功後,改回自訂 port
ports:
- "8888:80"
- "8889:443"

# 5. 重新啟動此專案
docker compose up -d

# 6. 恢復原本的服務
sudo systemctl start nginx

3、8-測試 HTTPS

• 開啟瀏覽器訪問 https://myapp.duckdns.org:8889

• 檢查瀏覽器網址列是否顯示鎖頭圖示

• 點擊鎖頭可查看憑證資訊

🎁 給想直接動手的你:完整範例 Repo

如果你看到這裡,代表你已經真正理解 HTTPS 這一整套在做什麼了 👏

我把這篇文章的內容,整理成一個可直接跑的 GitHub Repo,裡面包含:

• 🐳 完整 compose.yml

• 🔐 Nginx(HTTP → HTTPS)設定範例

• 🤖 Certbot 自動續期配置

• 🧪 可重現的測試流程與指令

• 📁 清楚的專案結構(不是只有一堆檔案)

🔗 GitHub Repo: https://github.com/weihanchen/docker-nginx-letsencrypt-certbot

✍️ 結語:HTTPS 不難,只是以前沒人好好講

HTTPS 真正困難的不是設定檔,而是:

• 不知道驗證在驗什麼

• 不知道為什麼一定要 HTTP

• 不知道為什麼大家一直 renew

希望這篇文章,可以讓你之後看到 HTTPS 時,心裡是:

😌「喔~我懂這一整套在幹嘛了」

留言
avatar-img
阿Han的沙龍
150會員
320內容數
哈囉,我是阿Han,是一位 👩‍💻 軟體研發工程師,喜歡閱讀、學習、撰寫文章及教學,擅長以圖代文,化繁為簡,除了幫助自己釐清思路之外,也希望藉由圖解的方式幫助大家共同學習,甚至手把手帶您設計出高品質的軟體產品。
阿Han的沙龍的其他內容
2023/04/23
Web Workers主要提供簡單的API讓網頁在背景執行緒中執行程式而不干擾使用者的操作。 javascript主要功能是與user操作頁面互動及操作dom,試想若使用多執行緒的概念,那麼一個動作是新增至某個dom節點,另一個動作則是修改該dom節點,此時瀏覽器應該使用哪個動作為準? 所以為了避免
Thumbnail
2023/04/23
Web Workers主要提供簡單的API讓網頁在背景執行緒中執行程式而不干擾使用者的操作。 javascript主要功能是與user操作頁面互動及操作dom,試想若使用多執行緒的概念,那麼一個動作是新增至某個dom節點,另一個動作則是修改該dom節點,此時瀏覽器應該使用哪個動作為準? 所以為了避免
Thumbnail
2023/04/23
Service worker與Web workers相同,也都是一段運行在瀏覽器後台的腳本,提供一些不需要與頁面直接交互的功能(操作dom),主要處理網路相關的問題,可以攔截網路請求進行相對應的優化動作,我們把它想像成與伺服器之間的代理服務器可能會比較容易理解,當網路環境不佳時便回應快取資源,待網路
Thumbnail
2023/04/23
Service worker與Web workers相同,也都是一段運行在瀏覽器後台的腳本,提供一些不需要與頁面直接交互的功能(操作dom),主要處理網路相關的問題,可以攔截網路請求進行相對應的優化動作,我們把它想像成與伺服器之間的代理服務器可能會比較容易理解,當網路環境不佳時便回應快取資源,待網路
Thumbnail
2023/04/23
Cookie簡介與個人隱私議題 在談Cookieless之前我們先來了解什麼是Cookie,這裡的Cookie並不是餅乾的意思,而是為了讓人們在網路上通訊時,能夠創造更無縫的體驗,想像一下,假設我們在使用網站時,每切換一頁就要進行登入一次,我想大部分的人都已經抓狂並放棄使用的吧! 而Cookie的出
Thumbnail
2023/04/23
Cookie簡介與個人隱私議題 在談Cookieless之前我們先來了解什麼是Cookie,這裡的Cookie並不是餅乾的意思,而是為了讓人們在網路上通訊時,能夠創造更無縫的體驗,想像一下,假設我們在使用網站時,每切換一頁就要進行登入一次,我想大部分的人都已經抓狂並放棄使用的吧! 而Cookie的出
Thumbnail
看更多
你可能也想看
Thumbnail
此篇我們會將Nginx log修改成為Logstash可以正確辨識的格式。
Thumbnail
此篇我們會將Nginx log修改成為Logstash可以正確辨識的格式。
Thumbnail
OWASP( Open Web Application Security Project) 專案維護核心規則集 Core Rule Set 縮寫 CRS 能抗衡一般類別的漏洞攻擊手法 CRS 在ModSecurity 官網免費釋出 當然也有需付費的規則集 。
Thumbnail
OWASP( Open Web Application Security Project) 專案維護核心規則集 Core Rule Set 縮寫 CRS 能抗衡一般類別的漏洞攻擊手法 CRS 在ModSecurity 官網免費釋出 當然也有需付費的規則集 。
Thumbnail
ModSecurity 是普遍應用之 公開網頁程式防火牆 ( 可 搭配 OWASP (Open Web Application Security Project) 維護的免費核心規則集 Core Rule Set CRS 初始設計 為 Apache HTTP Server 之模組 後續發展成 htt
Thumbnail
ModSecurity 是普遍應用之 公開網頁程式防火牆 ( 可 搭配 OWASP (Open Web Application Security Project) 維護的免費核心規則集 Core Rule Set CRS 初始設計 為 Apache HTTP Server 之模組 後續發展成 htt
Thumbnail
Recently, I add some useful settings for my nginx web server. I would like to take a note of some basic and useful settings of nginx. http server
Thumbnail
Recently, I add some useful settings for my nginx web server. I would like to take a note of some basic and useful settings of nginx. http server
Thumbnail
I’m sure that I would forget it again after a few months, so let’s take note of it. Installation We need to use homebrew to install certbot. renew
Thumbnail
I’m sure that I would forget it again after a few months, so let’s take note of it. Installation We need to use homebrew to install certbot. renew
Thumbnail
Introduction There are some differences between deploying a front-end framework project on the web server and deploying a back-end one. uWSGI Nginx
Thumbnail
Introduction There are some differences between deploying a front-end framework project on the web server and deploying a back-end one. uWSGI Nginx
Thumbnail
承上篇【NGINX 架構 - 事件驅動架構】後續,這篇開始記錄函數庫的相關筆記 事件驅動處理函數庫: 函數庫的細節,就沒打算太過深入,我們只要知道模型的運作方式、優缺點及如何使用就足夠了。 1. select模型: 手動啟用模組編譯: 禁用模組編譯: 2. poll模型: Nginx編譯代碼:
Thumbnail
承上篇【NGINX 架構 - 事件驅動架構】後續,這篇開始記錄函數庫的相關筆記 事件驅動處理函數庫: 函數庫的細節,就沒打算太過深入,我們只要知道模型的運作方式、優缺點及如何使用就足夠了。 1. select模型: 手動啟用模組編譯: 禁用模組編譯: 2. poll模型: Nginx編譯代碼:
Thumbnail
上一篇有講完【NGINX 架構 - 《模組化設計》】後,接下來這邊透過自己的理解,來專門介紹『事件趨動架構』,以此來幫助自己認識NGINX架構的內容。 什麼是「事件驅動架構」 而這邊指的事件驅動架構是一種程式設計的方式 - 事件驅動程式設計(Event-driven programming)
Thumbnail
上一篇有講完【NGINX 架構 - 《模組化設計》】後,接下來這邊透過自己的理解,來專門介紹『事件趨動架構』,以此來幫助自己認識NGINX架構的內容。 什麼是「事件驅動架構」 而這邊指的事件驅動架構是一種程式設計的方式 - 事件驅動程式設計(Event-driven programming)
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News