> ## Content Index
> Fetch the complete content index at: https://tbbbk.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# 纯公网 IP 申请 HTTPS 证书记录 (Docker + Lego + Let's Encrypt)
- URL: https://tbbbk.com/lets-encrypt-ip-ssl-certificate-docker-guide/
- Published: 2025-12-31T04:17:00.000Z
- Updated: 2026-01-30T05:31:38.000Z
- Author: TuBaiBai
- Tags: 随手记, #Import 2026-07-20 07:13

# 纯公网 IP 申请 HTTPS 证书指南 (Docker + Lego 自动续期)

最近折腾一些内部服务，不想绑定域名，直接用 IP 访问，但浏览器那个“不安全”的红色警告看着实在难受。于是研究了一下怎么给公网 IP 申请免费的 SSL 证书。

这里必须要提一个**重磅消息**：**Let's Encrypt 官方已于本月（2025年12月19日）正式支持公网 IP 证书签发**。这意味着我们不再需要去凑合使用 HiCA 或 ZeroSSL，直接用官方接口即可，稳得一匹。

本教程使用 Docker + Lego 工具来实现。这套方案最大的优点是**兼容机器上现有的 Nginx/Caddy 等服务**——只有在真正需要续期的那几秒钟才会短暂占用 80 端口，平时互不干扰。

## 1\. 搞定环境：Docker

有 Docker 的跳过这一步。没装的跑一下命令：

**非大陆服务器 (Github 直连)：**

Bash

```
curl -fsSL https://get.docker.com | sh
```

**国内服务器 (加速镜像)：**

Bash

```
bash <(curl -sSL https://linuxmirrors.cn/docker.sh)
```

## 2\. 首次手动申请

为了不污染宿主机环境，我们直接拉个 Lego 容器来跑。因为申请证书需要验证 80 端口，所以**如果你的 80 端口被占用了（比如跑着 Nginx），得先停一下**。

```
# 1. 暂停现有的 Web 服务 (根据你的实际情况改，没有就跳过)
# systemctl stop nginx
2. 定义一下变量，方便后面复用
DATA_DIR="$HOME/lego"
IP=$(curl -s -4 ip.sb)  # 自动获取公网IP
3. 启动 Lego 容器申请证书
这里的 --email 记得改成你自己的，虽然乱填也能过，但最好填真实的
docker run --rm -it 
-v "$DATA_DIR":/.lego 
-p 80:8888 
goacme/lego 
--email="example@gmail.com" 
--accept-tos 
--server="https://acme-v02.api.letsencrypt.org/directory" 
--http 
--http.port=":8888" 
--key-type="ec384" 
--domains="$IP" 
--disable-cn 
run --profile "shortlived" 
&& echo -e "\n✅ 申请成功! 证书位置如下:" 
&& echo "证书 (Public): $DATA_DIR/certificates/$IP.crt" 
&& echo "私钥 (Private): $DATA_DIR/certificates/$IP.key"
4. 恢复 Web 服务

```

> **小贴士**

## 3\. 编写智能续期脚本 (核心步骤)

Lego 虽然自带 renew 命令，但不管三七二十一直接停 Web 服务有点太暴力了。

所以在论坛找了个脚本：**先检查证书有效期，只有剩余时间不足 3 天时，才停止 Nginx 进行续期**。最大程度保证服务在线率。

创建脚本：`nano $HOME/auto_ssl.sh`

```
#!/bin/bash
--- 核心配置区 ---
证书存储目录 (务必使用绝对路径)
DATA_DIR="$HOME/lego"
EMAIL="example@gmail.com"
触发续期的剩余天数 (建议设为 3-7 天)
RENEW_DAYS=3
自动获取本机 IP
IP=$(curl -s -4 ip.sb)
CERT_PATH="$DATA_DIR/certificates/$IP.crt"
--- 核心逻辑 ---
check_validity() {
# 如果证书都没了，那必须续期
if [ ! -f "$CERT_PATH" ]; then
echo "❌ 未找到证书文件: $CERT_PATH"
return 1
fi
# 计算剩余天数
EXP_DATE=$(openssl x509 -enddate -noout -in "$CERT_PATH" | cut -d= -f2)
EXP_EPOCH=$(date -d "$EXP_DATE" +%s)
NOW_EPOCH=$(date +%s)
LEFT_SECONDS=$((EXP_EPOCH - NOW_EPOCH))
LEFT_DAYS=$((LEFT_SECONDS / 86400))

echo "📅 证书剩余有效期: $LEFT_DAYS 天 (续期阈值: $RENEW_DAYS 天)"

if [ "$LEFT_DAYS" -le "$RENEW_DAYS" ]; then
    return 1 # 时间不够了，要续期
else
    return 0 # 时间还早，继续睡
fi

}
1. 检查阶段
if check_validity; then
echo "✅ 证书坚挺中，无需操作，Web 服务保持运行。"
exit 0
fi
2. 续期阶段 (只有返回 1 才会走到这里)
echo "⚠️ 证书即将过期，开始执行续期流程..."
echo "🛑 正在暂停 Web 服务以释放 80 端口..."
--- 在这里修改你的停止命令 ---
systemctl stop nginx
---------------------------
echo "🚀 启动 Docker 容器进行续期..."
docker run --rm 
-v "$DATA_DIR":/.lego 
-p 80:8888 
goacme/lego 
--email="$EMAIL" 
--path="/.lego" 
--server="https://acme-v02.api.letsencrypt.org/directory" 
--http --http.port=":8888" 
--domains="$IP" 
--disable-cn 
renew --profile "shortlived" --days "$RENEW_DAYS" --reuse-key --no-random-sleep
LEGO_STATUS=$?
echo "▶️ 正在恢复 Web 服务..."
--- 在这里修改你的启动命令 ---
systemctl start nginx
---------------------------
3. 报告结果

```

记得给脚本加执行权限：

```
chmod +x $HOME/auto_ssl.sh
```

## 4\. 设个闹钟 (Crontab)

让服务器每天早上 5 点自己检查一遍。

输入 `crontab -e`，添加一行：

代码段

```
# 每天凌晨 5:00 执行 IP 证书续期检查
0 5 * * * /bin/bash /root/auto_ssl.sh >> /root/lego_renew.log 2>&1
```

*(注意：请将 `/root/auto_ssl.sh` 替换为你脚本的实际绝对路径)*

---

### 💡 避坑指南

1. **路径问题**：脚本里所有的路径（`DATA_DIR` 等）一定要用**绝对路径**，Crontab 执行时的环境和我不一样。
2. **Web 服务冲突**：脚本里的 `systemctl stop nginx` 是核心，如果你用的不是 Nginx（比如 Caddy、Apache 或者 Docker 跑的 Nginx），记得把那两行命令改掉。
3. **生效验证**：续期成功后，虽然证书文件更新了，但你的 Web 服务器可能加载的还是内存里的旧证书。**务必确保脚本里的重启/重载命令是有效的**。

搞定收工！以后这个 IP 的 HTTPS 就不用操心了。