Cloudflare DNS Only:非标准端口直连 HTTPS

将 Cloudflare 记录切换为 DNS Only 后,请求会直接到达源站,浏览器也会直接校验源站证书。要在 8443 等非标准端口上提供可信 HTTPS,只需通过 DNS-01 签发 Let’s Encrypt 证书,并由 Nginx 加载。

DNS-01 不依赖 80/443 端口,也不需要临时自签名证书。本文按 签发证书 → 配置 Nginx → 验证源站 → 切换 DNS 的顺序操作。

DNS Only 仅使用 Cloudflare 权威 DNS。源站 IP 将公开,CDN、WAF、Cloudflare Access 等代理能力也会失效。备案和网络接入要求请遵守当地法规及云厂商规则。

一、原理与准备

切换后的链路如下:

1
2
用户 → 源站:8443 → Nginx → 本地服务
Let’s Encrypt 证书

证书绑定域名而非端口,因此可用于 8443。DNS 只解析 IP,访问时仍需显式指定端口:

1
https://api1.example.com:8443/

本文以腾讯云 Ubuntu/Debian、两个域名和两个本地服务为例。先替换以下变量:

1
2
3
4
5
6
7
DOMAIN1=api1.example.com
DOMAIN2=api2.example.com
ORIGIN_IP=203.0.113.10
HTTPS_PORT=8443
NGINX_FILE=/etc/nginx/conf.d/example.conf
EMAIL=admin@example.com
CF_CREDENTIALS=/root/.secrets/certbot/cloudflare.ini

203.0.113.10 是文档示例地址,必须替换为源站公网 IP。

安装依赖:

1
2
sudo apt update
sudo apt install -y nginx certbot python3-certbot-dns-cloudflare

在腾讯云安全组中放行源站 TCP 8443;若启用了 UFW,再执行:

1
sudo ufw allow 8443/tcp

最终由浏览器直连,安全组不能只允许 Cloudflare IP。使用公网 IPv6 时,还需同步配置 IPv6 防火墙、Nginx 监听和 AAAA 记录。

二、签发 Let’s Encrypt 证书

1. 配置 Cloudflare API Token

进入 Cloudflare API Tokens → Create Token,使用 Edit zone DNS 模板,并将权限限制为目标 Zone 的 DNS: Edit。不要使用 Global API Key。

创建 Certbot 凭据文件:

1
2
3
4
sudo install -d -m 700 /root/.secrets/certbot
sudo touch "$CF_CREDENTIALS"
sudo chmod 600 "$CF_CREDENTIALS"
sudoedit "$CF_CREDENTIALS"

写入:

1
dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN

该文件用于自动续期,不要提交到仓库或在签发后删除。

2. 签发证书

1
2
3
4
5
6
7
8
for DOMAIN in "$DOMAIN1" "$DOMAIN2"; do
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials "$CF_CREDENTIALS" \
--dns-cloudflare-propagation-seconds 60 \
--cert-name "$DOMAIN" -d "$DOMAIN" \
--email "$EMAIL" --agree-tos --non-interactive
done

每个域名单独签发,便于续期和排错。DNS-01 可在记录仍为橙色云时完成,不会中断现有代理流量。

三、配置 Nginx

编辑配置:

1
sudoedit "$NGINX_FILE"

以下示例将两个域名分别转发到本地 1102511026

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
server {
listen 8443 ssl;
server_name api1.example.com;

ssl_certificate /etc/letsencrypt/live/api1.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api1.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;

client_max_body_size 50M;

location / {
proxy_pass http://127.0.0.1:11025;
proxy_http_version 1.1;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

server {
listen 8443 ssl;
server_name api2.example.com;

ssl_certificate /etc/letsencrypt/live/api2.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api2.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;

client_max_body_size 50M;

location / {
proxy_pass http://127.0.0.1:11026;
proxy_http_version 1.1;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

如无需 WebSocket,可删除 UpgradeConnection 两行。加载配置:

1
sudo nginx -t && sudo systemctl reload nginx

四、验证源站并切换 DNS

切换前使用 --resolve 绕过当前 DNS,直接验证源站、SNI 和证书:

1
2
3
4
5
6
7
for DOMAIN in "$DOMAIN1" "$DOMAIN2"; do
curl --noproxy '*' --connect-timeout 10 \
--resolve "${DOMAIN}:${HTTPS_PORT}:${ORIGIN_IP}" \
-sS -o /dev/null \
-w "domain=${DOMAIN} HTTP=%{http_code} remote=%{remote_ip} TLS=%{ssl_verify_result}\n" \
"https://${DOMAIN}:${HTTPS_PORT}/"
done

确认 remote 为源站 IP、TLS=0,且 HTTP 状态符合应用预期。不要使用 -k,否则无法验证证书。

若源站只允许 Cloudflare IP,或启用了 Authenticated Origin Pulls、Cloudflare Access 等边缘鉴权,应先调整限制,否则浏览器无法直连。

验证通过后,在 Cloudflare DNS → Records 中将记录改为灰色云:

类型 名称 内容 Proxy status
A api1 源站公网 IPv4 DNS only
A api2 源站公网 IPv4 DNS only

没有公网 IPv6 时,应删除对应的 AAAA 记录。等待 DNS 缓存更新后,即可访问:

1
2
https://api1.example.com:8443/
https://api2.example.com:8443/

DNS 无法将 443 自动映射到 8443。如需省略端口,入口服务必须监听 443

五、自动续期

创建 deploy hook,使 Nginx 在证书续期后自动重载:

1
2
3
4
5
6
7
8
9
sudo install -d -m 755 /etc/letsencrypt/renewal-hooks/deploy
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh >/dev/null <<'EOF'
#!/bin/sh
nginx -t && systemctl reload nginx
EOF
sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

sudo systemctl enable --now certbot.timer
sudo certbot renew --dry-run --run-deploy-hooks

保留 Cloudflare 凭据文件及 Token;修改 Token 后需同步更新文件。

参考资料