最近把 Windows NAS 上的一些 Web 服务重新整理了一遍。

之前很多服务直接通过 HTTP 或公网端口访问,相当于一直“裸奔”。这次决定统一使用:

1
2
3
4
5
6
7
8
9
10
11
Cloudflare DNS

Let's Encrypt

DNS-01 验证

win-acme 自动申请 / 续期

Apache HTTPS

反向代理内部服务

这样不仅可以给现有服务启用 HTTPS,以后增加新的子域名也不需要每次重新申请一张证书。

本文记录 Windows 环境下使用 Apache、win-acme 和 Cloudflare DNS 自动申请及续期 Let’s Encrypt 通配符证书的完整流程。

一、环境

示例环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
系统:
Windows 10 / Windows 11

Apache:
D:\Server\Apache24

Apache 主程序:
D:\Server\Apache24\bin\httpd.exe

win-acme:
D:\Tools\win-acme

证书输出目录:
D:\Tools\win-acme\certs

本文统一使用以下示例域名:

1
example.com

例如可能存在:

1
2
3
4
nas.example.com
panel.example.com
files.example.com
media.example.com

相比给每个域名单独申请证书,更适合直接申请:

1
*.example.com

如果根域名本身也需要 HTTPS,则证书里同时加入:

1
2
example.com
*.example.com

注意:

1
*.example.com

可以覆盖:

1
2
3
nas.example.com
panel.example.com
files.example.com

但不能覆盖:

1
example.com

也不能覆盖:

1
a.b.example.com

二、为什么使用 DNS-01

Let’s Encrypt 常见验证方式包括:

1
2
HTTP-01
DNS-01

普通单域名可以使用 HTTP-01,但通配符证书:

1
*.example.com

必须使用 DNS-01。

DNS-01 的原理是临时创建类似下面的 TXT 记录:

1
_acme-challenge.example.com

Let’s Encrypt 查询到正确内容后,就确认申请者拥有这个域名。

如果域名 DNS 已经托管在 Cloudflare,就可以让 win-acme 使用 Cloudflare API 自动完成:

1
2
3
4
创建 TXT
→ 等待 DNS 生效
→ Let's Encrypt 验证
→ 删除 TXT

这样续期也可以完全自动完成。


三、Cloudflare 创建 API Token

不要直接使用 Cloudflare Global API Key。

推荐单独创建权限尽可能小的 API Token。

进入 Cloudflare Dashboard:https://dash.cloudflare.com/profile/api-tokens

1
2
3
My Profile
→ API Tokens
→ Create Token

创建自定义 Token。

DNS 权限设置为:

1
2
3
Zone
DNS
Edit

另外建议加入:

1
2
3
Zone
Zone
Read

资源范围只允许操作需要申请证书的 Zone,例如:

1
example.com

最终权限类似:

1
2
3
4
5
6
7
8
9
10
Permissions:

Zone / DNS / Edit
Zone / Zone / Read

Zone Resources:

Include
Specific zone
example.com

创建完成后 Cloudflare 只会显示一次 Token。

一定保存好。

不要把 API Token 公开

四、安装 win-acme Cloudflare 插件

如果使用 win-acme 的 Pluggable 版本,需要确认已经安装 Cloudflare DNS Validation Plugin。

例如插件文件可能位于:

1
D:\Tools\win-acme\PKISharp.WACS.Plugins.ValidationPlugins.Cloudflare.dll

启动:

1
2
cd D:\Tools\win-acme
.\wacs.exe

如果插件加载正常,创建证书时就可以看到 Cloudflare DNS 验证方式。


五、使用 win-acme 创建新的证书任务

首先以管理员身份打开 CMD 或 PowerShell,然后启动 win-acme。

假设 win-acme 位于:

1
D:\Tools\win-acme

可以直接运行:

1
D:\Tools\win-acme\wacs.exe

也可以先进入目录:

1
2
cd /d D:\Tools\win-acme
wacs.exe

启动后会看到类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
A simple Windows ACMEv2 client (WACS)
Software version 2.2.9.1701 (release, pluggable, standalone, 64-bit)
Connecting to https://acme-v02.api.letsencrypt.org/...
Connection OK!
Scheduled task looks healthy
Please report issues at https://github.com/win-acme/win-acme

N: Create certificate (default settings)
M: Create certificate (full options)
R: Run renewals (0 currently due)
A: Manage renewals (1 total)
O: More options...
Q: Quit

这里可以先确认几件事情。

1
Connection OK!

表示 win-acme 已经能够正常连接 Let’s Encrypt 的 ACME 服务。

1
Scheduled task looks healthy

表示 win-acme 的自动续期计划任务目前正常。

如果还能看到:

1
A: Manage renewals (1 total)

说明当前机器已经存在至少一个旧的证书续期任务。

这不会影响创建新的通配符证书。


5.1 不要选默认模式 N,选择完整配置 M

主菜单中有:

1
2
N: Create certificate (default settings)
M: Create certificate (full options)

这里选择:

1
M

然后回车。

即:

1
Please choose from the menu: M

之所以不使用:

1
N

是因为这里需要自己明确控制:

1
2
3
4
5
6
域名
DNS-01 验证
Cloudflare DNS 插件
PEM 文件输出
证书保存位置
证书续期后执行 Apache graceful reload

N 使用的是默认流程,更适合简单的网站自动配置。

对于:

1
2
3
4
5
6
7
Windows
+
Apache
+
Cloudflare DNS
+
Wildcard certificate

这种组合,使用:

1
M: Create certificate (full options)

更合适。


5.2 选择证书目标来源

进入 Full options 后,win-acme 会询问证书的域名从哪里获取。

界面通常类似:

1
2
3
4
5
6
7
8
9
Please specify how the list of domain names that will be included in the
certificate should be determined. If you choose for one of the "all bindings"
options, the list will automatically be updated for future renewals to
reflect the bindings at that time.

1: Read bindings from IIS
2: Manual input
3: CSR created by another program
C: Abort

由于这里使用的是:

1
Apache

而不是 IIS,因此不能使用:

1
Read bindings from IIS

应该选择:

1
2

也就是:

1
Manual input

然后回车。


5.3 手动输入需要申请证书的域名

接下来 win-acme 会要求输入证书需要包含的域名。

例如可能出现:

1
Enter comma-separated list of host names, starting with the common name:

如果只需要通配符证书,可以填写:

1
*.example.com

但更推荐把根域名也一起放进去:

1
example.com,*.example.com

例如:

1
2
Enter comma-separated list of host names, starting with the common name:
example.com,*.example.com

注意多个域名之间使用:

1
,

也就是英文半角逗号分隔。

不要写成:

1
example.com *.example.com

也不要使用中文逗号。

推荐:

1
example.com,*.example.com

5.4 为什么建议同时申请根域名和通配符

单独申请:

1
*.example.com

只能覆盖一级子域名,例如:

1
2
3
nas.example.com
panel.example.com
files.example.com

但是它不能覆盖:

1
example.com

因此如果未来根域名本身也可能建立网站,最好直接申请:

1
2
example.com
*.example.com

最终是一张包含两个 SAN 的证书:

1
2
example.com
*.example.com

这样以后就不需要再单独给根域名申请证书。


5.5 检查 win-acme 识别到的域名

输入完成后,win-acme 通常会显示识别到的域名,例如:

1
Target generated using plugin Manual: example.com and 1 alternatives

或者类似的信息。

这里一定检查是否同时包含:

1
2
example.com
*.example.com

如果发现:

1
2
3
拼写错误
域名层级错误
漏掉通配符 *

不要继续。

直接退出本次创建任务并重新进入即可。

特别要注意:

1
*.example.com

最前面的:

1
*.

不能漏。


5.6 Common Name 如何处理

某些版本或配置下,win-acme 可能继续询问:

1
Friendly name

或者 Common Name 相关内容。

对于包含:

1
2
example.com
*.example.com

的证书,不需要过度关注传统的 Common Name。

现代 HTTPS 证书实际主要依赖:

1
Subject Alternative Name

也就是 SAN。

只要最后证书中的 SAN 包含:

1
2
example.com
*.example.com

即可。

如果 win-acme 自动使用第一个域名:

1
example.com

作为主名称,保持默认即可。


5.7 是否拆分成多张证书

后面的 Full options 流程中,win-acme 可能询问是否:

1
Create separate certificates

或询问如何拆分不同域名。

对于:

1
2
example.com
*.example.com

这种情况,我们的目标就是:

一张证书同时包含根域名和 wildcard。

因此不要拆成两张证书。

保持:

1
Single certificate

即可。

最终应该是:

1
2
3
Certificate
├── example.com
└── *.example.com

而不是:

1
2
3
4
5
Certificate 1
└── example.com

Certificate 2
└── *.example.com

5.8 到这里暂时不要继续乱选

完成域名输入以后,下一阶段才是真正重要的:

1
验证方式

也就是:

1
2
HTTP-01
DNS-01

由于这里申请的是:

1
*.example.com

所以后面必须使用:

1
DNS-01

并进一步选择:

1
Cloudflare DNS

不要选择:

1
2
3
HTTP validation
Self-hosting
Apache webroot

因为 Let’s Encrypt 的 wildcard certificate 必须通过 DNS-01 验证。

因此第五步做到这里,正确的状态应该是:

1
2
3
4
5
6
7
8
9
10
11
12
13
启动 win-acme

选择 M - Full options

选择 Manual input

输入 example.com,*.example.com

确认域名正确

保持为一张证书

准备进入 DNS-01 validation

下一节再配置:

1
2
3
Cloudflare DNS-01
API Token
TXT 自动验证

5.9 本步骤常见错误

错误一:选择 N

1
N: Create certificate (default settings)

虽然不一定完全不能用,但会让后面很多选项由 win-acme 自动决定。

本教程为了明确控制 Cloudflare DNS、PEM 输出以及 Apache reload,使用:

1
M

错误二:只输入普通子域名

例如:

1
nas.example.com

这样得到的仍然只是单域名证书。

如果目标是以后所有一级子域名共用一张证书,必须包含:

1
*.example.com

错误三:以为 wildcard 包含根域名

1
*.example.com

并不包括:

1
example.com

需要的话必须一起写:

1
example.com,*.example.com

错误四:使用 HTTP-01 验证 wildcard

后续如果看到:

1
HTTP validation

不要选择。

Wildcard certificate:

1
*.example.com

必须使用:

1
DNS-01

错误五:把 Cloudflare 橙云关掉

申请 DNS-01 证书时,一般没有必要为了验证临时关闭 Cloudflare Proxy。

因为验证的是:

1
_acme-challenge.example.com TXT

不是访问:

1
https://example.com

因此:

1
2
Proxied
DNS only

通常都不影响这一阶段的 DNS-01 验证。


六、使用 Cloudflare DNS-01 完成域名验证并导出 PEM 证书

完成上一节的域名输入后,win-acme 会询问如何证明自己拥有这些域名。因为这里申请的是通配符证书,所以必须选择 DNS-01;如果已经安装 Cloudflare 插件,菜单中会直接出现:

1
2
3
4
5
6
7
8
9
10
11
12
13
1: [http] Save verification files on (network) path
2: [http] Serve verification files from memory
3: [http] Upload verification files via FTP(S)
4: [http] Upload verification files via SSH-FTP
5: [http] Upload verification files via WebDav
6: [dns] Create verification records in Cloudflare DNS
7: [dns] Create verification records manually (auto-renew not possible)
8: [dns] Create verification records with acme-dns
9: [dns] Create verification records with your own script
10: [tls-alpn] Answer TLS verification request from win-acme
<Enter>: Abort

How would you like prove ownership for the domain(s)?: 6

这里选择:

1
6

即:

1
[dns] Create verification records in Cloudflare DNS

不要选择第 7 项手动创建 DNS 记录。虽然手动 DNS-01 也可以申请通配符证书,但 win-acme 已经明确提示:

1
auto-renew not possible

也就是说以后每次续期都需要人工修改 TXT 记录。使用 Cloudflare API 后,TXT 记录的创建和删除都可以自动完成,才能真正实现无人值守续期。

6.1 输入 Cloudflare API Token

随后会要求提供 Cloudflare API Token:

1
2
3
4
5
6
Description:         API Token for Cloudflare.

1: Type/paste in console
2: Search in vault

Choose from the menu: 1

第一次配置时选择:

1
1

然后粘贴上一节在 Cloudflare 创建的 API Token:

1
Cloudflare API Token: *****************************************************

输入内容不会明文显示,这是正常现象。

win-acme 接着会询问是否把 Token 保存进自己的 Secret Vault:

1
Save to vault for future reuse? (y/n*) - yes

建议选择:

1
y

这样以后自动续期时不需要重新输入 Token。

随后给这个 Secret 设置一个便于识别的名称,例如:

1
2
Please provide a unique name to reference this secret:
cloudflare-wildcard-token

这里建议名称也全部使用英文、数字和连字符,不要把真实域名、个人信息或者 API Token 本身写进名称。

例如:

1
cloudflare-wildcard-token

即可。

API Token 本身一定不要写进博客、截图或 Git 仓库。


6.2 私钥类型选择 RSA

域名验证方式配置完成后,win-acme 会询问证书使用什么类型的私钥:

1
2
3
4
5
6
7
8
9
10
After ownership of the domain(s) has been proven, we will create a
Certificate Signing Request (CSR) to obtain the actual certificate. The CSR
determines properties of the certificate like which (type of) key to use. If
you are not sure what to pick here, RSA is the safe default.

1: Elliptic Curve key
2: RSA key
C: Abort

What kind of private key should be used for the certificate?: 2

这里选择:

1
2

也就是:

1
RSA key

ECC 证书同样可以正常使用,而且密钥更短,但如果目标是兼容 Apache、各种客户端和旧设备,RSA 是最省心的选择。

因此本文统一使用:

1
RSA

6.3 将证书导出为 PEM 文件

证书签发后,win-acme 会询问保存方式:

1
2
3
4
5
6
7
1: IIS Central Certificate Store (.pfx per host)
2: PEM encoded files (Apache, nginx, etc.)
3: PFX archive
4: Windows Certificate Store (Local Computer)
5: No (additional) store steps

How would you like to store the certificate?: 2

因为这里使用 Apache,所以选择:

1
2

即:

1
PEM encoded files (Apache, nginx, etc.)

然后 win-acme 会要求填写 .pem 文件的输出目录:

1
2
3
Description:         .pem files are exported to this folder.

File path:

这里有一个实际踩过的坑:

证书保存路径不要包含中文字符。

例如下面这种路径不要使用:

1
D:\Server\Apache24\conf\ssl\通配符

实际配置时 win-acme 可能直接报:

1
2
Directory ... does not exist
Invalid input: invalid path

因此目录名统一使用 ASCII 字符,例如:

1
D:\Server\Apache24\conf\ssl\wildcard

本文后续统一使用:

1
D:\Server\Apache24\conf\ssl\wildcard

除了证书目录,整个 Apache、win-acme 和自动化脚本相关路径最好都不要使用中文,以免以后某个程序、脚本或计划任务对 Unicode 路径处理不一致。


6.4 PEM 私钥不设置密码

随后会询问是否给导出的 Private Key 设置密码:

1
2
3
4
5
6
7
Description:         Password to set for the private key .pem file.

1: None
2: Type/paste in console
3: Search in vault

Choose from the menu: 1

这里选择:

1
1

也就是:

1
None

原因不是“不加密码更安全”,而是 Apache 需要在 Windows 开机后自动启动。

如果给私钥设置密码,Apache 每次读取证书时可能需要人工输入密码,这会破坏:

1
2
3
开机自动启动
自动续期
自动重新加载证书

这套无人值守流程。

因此用于服务器自动运行的 PEM Private Key 通常保持无密码,然后通过 Windows 文件权限保护证书目录。


6.5 不需要再保存其他格式

第一次保存为 PEM 后,win-acme 会继续询问:

1
2
3
4
5
6
7
1: IIS Central Certificate Store (.pfx per host)
2: PEM encoded files (Apache, nginx, etc.)
3: PFX archive
4: Windows Certificate Store (Local Computer)
5: No (additional) store steps

Would you like to store it in another way too?: 5

如果只是给 Apache 使用,不需要再生成 PFX,也不需要额外导入 Windows Certificate Store,因此选择:

1
5

即:

1
No (additional) store steps

这样最终只维护 Apache 真正需要的 PEM 文件即可。


6.6 Installation Step 暂时选择 No

证书保存方式配置完成后,win-acme 会询问是否自动更新应用程序:

1
2
3
4
5
6
7
8
9
10
11
Installation plugin IIS not available: No IIS sites detected.

With the certificate saved to the store(s) of your choice, you may choose one
or more steps to update your applications, e.g. to configure the new
thumbprint, or to update bindings.

1: Create or update bindings in IIS
2: Start external script or program
3: No (additional) installation steps

Which installation step should run first?: 3

这里因为使用的是 Apache,不是 IIS,所以:

1
Installation plugin IIS not available

完全正常。

第一次配置时可以先选择:

1
3

即:

1
No (additional) installation steps

先确保:

1
2
3
4
Cloudflare DNS 验证成功
证书正常签发
PEM 正常生成
Apache 能正常读取证书

之后再单独配置续期完成后自动执行:

1
httpd.exe -k graceful

这样排错更简单,不需要第一次申请时同时处理证书和 Apache 自动重载两个问题。


6.7 Cloudflare DNS-01 自动验证过程

完成以上配置后,win-acme 会正式向 Let’s Encrypt 创建订单。

例如:

1
2
Plugin Manual generated source *.example.com with 2 identifiers
Plugin Single created 1 order

如果上一节同时填写了:

1
example.com,*.example.com

那么:

1
2 identifiers

是正常的,因为这张证书同时包含:

1
2
example.com
*.example.com

接下来 win-acme 会依次完成两个域名的 DNS-01 验证:

1
2
3
[*.example.com] Authorizing...
[*.example.com] Authorizing using dns-01 validation (Cloudflare)
[*.example.com] Record ******** successfully created

这里表示 win-acme 已经通过 Cloudflare API 自动创建了:

1
_acme-challenge.example.com

对应的 TXT 记录。

第一次查询时可能会看到:

1
2
3
No TXT records found
Preliminary validation failed on all nameservers
Will retry in 30 seconds (retry 1/10)...

看到这里不要退出,也不要立刻去手动修改 Cloudflare DNS。

这通常只是:

1
2
3
4
5
6
7
Cloudflare API 已创建记录

权威 DNS 节点尚未全部同步

win-acme 第一次查询太快

暂时查不到 TXT

win-acme 会自动等待并重试。

正常情况下稍后会出现:

1
2
Preliminary validation succeeded
Authorization result: valid

然后自动删除刚才创建的验证记录:

1
Record ******** deleted

如果证书同时包含根域名,接下来还会再执行一次:

1
2
[example.com] Authorizing...
[example.com] Authorizing using dns-01 validation (Cloudflare)

这时候偶尔可能看到:

1
2
3
Incorrect TXT record(s) found
Preliminary validation failed on all nameservers
Will retry in 30 seconds...

这同样不一定代表配置错误。

一个常见原因是前一个 DNS-01 challenge 的 TXT 记录刚刚删除,某些 DNS 节点缓存里暂时还保留旧值,而新的 challenge 已经创建。

只要随后出现:

1
2
Preliminary validation succeeded
Authorization result: valid

就说明完全正常,无需人工干预。


6.8 判断证书是否申请成功

两个 identifier 都验证成功后,会看到:

1
2
3
4
5
6
7
Downloading certificate [Manual] *.example.com
Store with PemFiles...
Exporting .pem files to D:\Server\Apache24\conf\ssl\wildcard
Scheduled task looks healthy
Adding renewal for [Manual] *.example.com
Next renewal due after ...
Certificate [Manual] *.example.com created

其中最重要的是最后一行:

1
Certificate [Manual] *.example.com created

看到它就表示证书已经成功签发。

同时:

1
Adding renewal for [Manual] *.example.com

表示 win-acme 已经建立了自动续期配置。

主菜单中的 renewal 数量也会增加,例如原来:

1
A: Manage renewals (1 total)

申请成功后可能变成:

1
A: Manage renewals (2 total)

这说明新的 wildcard certificate 已经进入 win-acme 的 renewal 管理。


6.9 检查实际生成的 PEM 文件

申请成功后,先不要急着修改 Apache。

先检查:

1
D:\Server\Apache24\conf\ssl\wildcard

例如 CMD:

1
dir D:\Server\Apache24\conf\ssl\wildcard

win-acme 的 PEM Storage 通常会生成若干 .pem 文件,例如:

1
2
3
4
example.com-chain-only.pem
example.com-chain.pem
example.com-crt.pem
example.com-key.pem

具体文件名以实际生成结果为准,不要在 Apache 配置中凭空猜文件名。

后续通常重点使用:

1
2
*-chain.pem
*-key.pem

分别对应完整证书链和 Private Key。


6.10 本步骤最终选择速查

以后重新配置时,如果不想重新阅读整节,可以直接参考:

1
2
3
4
5
6
7
8
9
10
Domain validation       → 6  Cloudflare DNS
Cloudflare Token → 1 Type/paste in console
Save token to vault → y
Vault secret name → cloudflare-wildcard-token
Private key → 2 RSA
Certificate store → 2 PEM encoded files
PEM directory → D:\Server\Apache24\conf\ssl\wildcard
PEM private key password→ 1 None
Additional store → 5 No
Installation step → 3 No

成功标志:

1
2
3
4
5
Authorization result: valid
Downloading certificate
Exporting .pem files
Adding renewal
Certificate ... created

如果验证过程中只是短暂出现:

1
No TXT records found

或者:

1
Incorrect TXT record(s) found

但随后自动重试并出现:

1
2
Preliminary validation succeeded
Authorization result: valid

则属于正常的 DNS 传播过程,不需要处理。


七、证书保存为 PEM

Apache 使用 PEM 文件比较方便。

在 win-acme 的证书存储方式中选择:

1
PEM encoded files

输出目录例如:

1
D:\Tools\win-acme\certs

生成后可能看到:

1
2
3
4
5
D:\Tools\win-acme\certs\
├── example.com-chain-only.pem
├── example.com-chain.pem
├── example.com-crt.pem
└── example.com-key.pem

Apache 通常主要使用:

1
2
example.com-chain.pem
example.com-key.pem

实际文件名以 win-acme 输出结果为准。

可以查看:

1
dir D:\Tools\win-acme\certs

八、Apache 启用 SSL

打开:

1
D:\Server\Apache24\conf\httpd.conf

确认 SSL 模块已经加载:

1
2
LoadModule ssl_module modules/mod_ssl.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so

如果还要做反向代理,通常还需要:

1
2
3
4
5
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule headers_module modules/mod_headers.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so

具体以自己的 Apache 环境为准。


九、Apache 使用 Let’s Encrypt 证书

假设证书文件为:

1
2
D:\Tools\win-acme\certs\example.com-chain.pem
D:\Tools\win-acme\certs\example.com-key.pem

Apache 可以这样配置:

1
2
3
4
5
6
7
8
9
10
<VirtualHost *:443>

ServerName nas.example.com

SSLEngine on

SSLCertificateFile "D:/Tools/win-acme/certs/example.com-chain.pem"
SSLCertificateKeyFile "D:/Tools/win-acme/certs/example.com-key.pem"

</VirtualHost>

注意 Windows 下 Apache 配置路径建议写成:

1
D:/Tools/...

而不是:

1
D:\Tools\...

十、一张通配符证书给多个子域名使用

假设后端服务分别运行在:

1
2
3
127.0.0.1:5000
127.0.0.1:6000
127.0.0.1:7000

则多个 VirtualHost 可以全部引用同一张通配符证书。

例如:

1
2
3
4
5
6
7
8
9
10
11
<VirtualHost *:443>
ServerName nas.example.com

SSLEngine on

SSLCertificateFile "D:/Tools/win-acme/certs/example.com-chain.pem"
SSLCertificateKeyFile "D:/Tools/win-acme/certs/example.com-key.pem"

ProxyPass / http://127.0.0.1:5000/
ProxyPassReverse / http://127.0.0.1:5000/
</VirtualHost>

另一个服务:

1
2
3
4
5
6
7
8
9
10
11
<VirtualHost *:443>
ServerName panel.example.com

SSLEngine on

SSLCertificateFile "D:/Tools/win-acme/certs/example.com-chain.pem"
SSLCertificateKeyFile "D:/Tools/win-acme/certs/example.com-key.pem"

ProxyPass / http://127.0.0.1:6000/
ProxyPassReverse / http://127.0.0.1:6000/
</VirtualHost>

再例如:

1
2
3
4
5
6
7
8
9
10
11
<VirtualHost *:443>
ServerName files.example.com

SSLEngine on

SSLCertificateFile "D:/Tools/win-acme/certs/example.com-chain.pem"
SSLCertificateKeyFile "D:/Tools/win-acme/certs/example.com-key.pem"

ProxyPass / http://127.0.0.1:7000/
ProxyPassReverse / http://127.0.0.1:7000/
</VirtualHost>

整体结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Internet

Cloudflare

Apache :443

*.example.com

├── nas.example.com
│ ↓
│ 127.0.0.1:5000

├── panel.example.com
│ ↓
│ 127.0.0.1:6000

└── files.example.com

127.0.0.1:7000

以后增加新服务时,只需要:

1
2
3
1. 添加 DNS
2. 添加 Apache VirtualHost
3. 继续使用现有 wildcard certificate

不需要重新申请证书。


十一、反向代理时传递 HTTPS 信息

如果 Apache 前端是 HTTPS,而后端程序是 HTTP:

1
2
3
4
5
客户端
↓ HTTPS
Apache
↓ HTTP
127.0.0.1

建议添加:

1
RequestHeader set X-Forwarded-Proto "https"

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<VirtualHost *:443>

ServerName panel.example.com

SSLEngine on

SSLCertificateFile "D:/Tools/win-acme/certs/example.com-chain.pem"
SSLCertificateKeyFile "D:/Tools/win-acme/certs/example.com-key.pem"

RequestHeader set X-Forwarded-Proto "https"

ProxyPass / http://127.0.0.1:6000/
ProxyPassReverse / http://127.0.0.1:6000/

</VirtualHost>

这样后端应用就更容易正确识别原始访问协议为 HTTPS。


十二、WebSocket 反向代理

部分面板、实时日志、终端等服务会使用 WebSocket。

如果普通页面正常,但实时功能异常,可以检查 WebSocket 代理。

例如:

1
2
3
4
5
6
7
RewriteEngine On

RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://127.0.0.1:6000/$1 [P,L]

ProxyPass / http://127.0.0.1:6000/
ProxyPassReverse / http://127.0.0.1:6000/

十三、测试 Apache 配置

每次修改 Apache 配置后,建议先测试语法:

1
D:\Server\Apache24\bin\httpd.exe -t

正常应该显示:

1
Syntax OK

确认无误后再平滑重新加载:

1
D:\Server\Apache24\bin\httpd.exe -k graceful

相比直接结束 Apache 进程,graceful 更适合重新加载配置和证书。


十四、配置证书自动续期后刷新 Apache

Let’s Encrypt 证书需要定期续期。

win-acme 会通过 Windows 计划任务自动检查证书。

但证书文件更新之后,Apache 也需要重新加载,否则运行中的 Apache 进程可能继续使用旧证书。

可以配置续期成功后执行:

1
2
3
程序:

D:\Server\Apache24\bin\httpd.exe

参数:

1
-k graceful

等价于:

1
D:\Server\Apache24\bin\httpd.exe -k graceful

完整续期流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
win-acme 定期检查

证书需要续期

Cloudflare DNS-01

自动创建 TXT

Let's Encrypt 签发新证书

覆盖 PEM 文件

httpd.exe -k graceful

Apache 加载新证书

十五、检查自动续期任务

打开:

1
Task Scheduler

检查 win-acme 创建的 renewal task 是否存在。

也可以重新运行:

1
D:\Tools\win-acme\wacs.exe

进入 renewal 管理菜单查看现有证书。

如果之前还保留有旧的单域名证书 renewal,在确认所有 VirtualHost 都已经切换到 wildcard certificate 后,再决定是否删除旧 renewal。


十六、Cloudflare SSL 模式

如果某个子域名开启 Cloudflare 橙云代理:

1
2
3
4
5
客户端

Cloudflare

源服务器

推荐 Cloudflare 设置:

1
2
3
SSL/TLS
→ Encryption mode
→ Full (strict)

这样是:

1
2
3
4
5
客户端
↓ HTTPS
Cloudflare
↓ HTTPS
Apache

Cloudflare 与客户端之间使用 Cloudflare Edge Certificate。

Cloudflare 与源站 Apache 之间,则使用服务器上的 Let’s Encrypt 证书。

两者不会冲突。


十七、Cloudflare 橙云和通配符证书不会冲突

例如:

1
2
3
nas.example.com      Proxied
panel.example.com Proxied
files.example.com DNS only

同时 Apache 使用:

1
*.example.com

完全没有问题。

橙云开启时:

1
2
3
4
5
6
7
8
9
Browser

Cloudflare Edge Certificate

Cloudflare

Let's Encrypt wildcard certificate

Apache

灰云时:

1
2
3
4
5
Browser

Let's Encrypt wildcard certificate

Apache

所以服务器端配置正式受信任的通配符证书仍然很有意义。


十八、Let’s Encrypt 与 Cloudflare Origin Certificate

如果源站只允许 Cloudflare 访问,可以考虑 Cloudflare Origin Certificate。

但如果服务器既可能:

1
经过 Cloudflare

又可能:

1
直接访问源站

更适合使用公开受信任的:

1
Let's Encrypt

Let’s Encrypt 可以同时用于:

1
浏览器 → Apache

和:

1
浏览器 → Cloudflare → Apache

因此通用性更高。


十九、检查当前证书

浏览器可以访问:

1
https://nas.example.com

查看地址栏中的证书信息。

如果开启 Cloudflare 橙云,浏览器看到的一般是 Cloudflare Edge Certificate,而不是 Apache 本机的 Let’s Encrypt 证书。

需要检查源站实际返回的证书时,可以使用 OpenSSL:

1
openssl s_client -connect SERVER_IP:443 -servername nas.example.com

重点检查:

1
2
3
4
subject
issuer
notBefore
notAfter

二十、常用排错命令

检查 Apache 配置:

1
D:\Server\Apache24\bin\httpd.exe -t

平滑重新加载:

1
D:\Server\Apache24\bin\httpd.exe -k graceful

查看 443 端口:

1
netstat -ano | findstr ":443"

查看证书目录:

1
dir D:\Tools\win-acme\certs

启动 win-acme:

1
D:\Tools\win-acme\wacs.exe

二十一、推荐架构

最终结构可以整理为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
              Internet


Cloudflare

HTTPS / 443


Windows NAS

Apache

*.example.com

┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
Service A Service B Service C
:5000 :6000 :7000

外部统一访问:

1
https://subdomain.example.com

内部后端仍然可以使用:

1
http://127.0.0.1:PORT

HTTPS、证书、域名和反向代理全部集中交给 Apache 管理。


二十二、新增一个 HTTPS 服务

假设以后增加:

1
media.example.com

后端运行:

1
127.0.0.1:8000

先添加 DNS,然后配置 Apache:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<VirtualHost *:443>

ServerName media.example.com

SSLEngine on

SSLCertificateFile "D:/Tools/win-acme/certs/example.com-chain.pem"
SSLCertificateKeyFile "D:/Tools/win-acme/certs/example.com-key.pem"

RequestHeader set X-Forwarded-Proto "https"

ProxyPass / http://127.0.0.1:8000/
ProxyPassReverse / http://127.0.0.1:8000/

</VirtualHost>

检查:

1
D:\Server\Apache24\bin\httpd.exe -t

出现:

1
Syntax OK

再执行:

1
D:\Server\Apache24\bin\httpd.exe -k graceful

完成。

不需要:

1
2
3
重新申请证书
重新做 DNS-01
单独给每个服务配置 SSL

二十三、排错顺序

如果 HTTPS 突然异常,可以按照下面顺序检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
① DNS 是否正确

② Cloudflare 橙云 / 灰云状态

③ Apache 是否监听 443

④ VirtualHost ServerName 是否正确

⑤ PEM 文件是否存在

⑥ httpd.exe -t 是否 Syntax OK

⑦ win-acme renewal 是否成功

⑧ Apache 是否在续期后 graceful reload

如果只是新增:

1
xxx.example.com

并且已经有:

1
*.example.com

通常不需要重新申请证书。


总结

整套方案的核心是:

1
2
3
4
5
6
7
8
9
Cloudflare
+
DNS-01
+
Let's Encrypt wildcard certificate
+
win-acme
+
Apache

第一次配置完成以后,后续维护成本很低。

尤其是配置好:

1
2
3
Cloudflare API Token
win-acme 自动续期
httpd.exe -k graceful

以后证书续期基本可以自动完成。

新增 Web 服务时,通常只需要:

1
2
添加 DNS
添加 Apache VirtualHost

就能直接使用 HTTPS。