💰 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會員
324內容數
哈囉,我是阿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
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
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編譯代碼:
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News