查询 35 次,耗时 0.2486 秒

从域名解析到 HTTPS 上线:Ubuntu + Nginx 的可验证部署与安全加固实践

jj1010223

New member

jj1010223 Rep
0
0
0
Rep
0
jj1010223 Vouches
Vouches
0
3 MONTHS
3 3 MONTHS OF SERVICE
jj1010223 获得
0
0
0
获得
0
jj1010223 凭证
0
0
0
凭证
0
主题
3
点赞
0
HakB
3 月
在社区成长 3 月
LEVEL 1 1 XP
 
很多网站上线问题,表面看是“服务器访问不了”,实际可能发生在完全不同的层:DNS 指向错误、AAAA 记录指向不可用的 IPv6、云安全组未放行、Nginx 虚拟主机匹配错误,或者证书续期后服务没有重新加载。

这篇文章以一台 Ubuntu 24.04 LTS 服务器、一个域名和一个静态站点为例,建立一条可检查、可回退的部署链路。重点不是堆配置,而是让 DNS、网络、Web 服务和证书生命周期各自有明确的验证方法。后续部署文档站、下载页或作为反向代理入口,都可以沿用这套思路。

一、背景与原理:一次 HTTPS 请求经过哪些环节

浏览器访问 https://example.com,大体经过以下步骤:

1. DNS 将域名解析为 A 记录中的 IPv4,或 AAAA 记录中的 IPv6。
2. 客户端连接对应地址的 TCP 443 端口。
3. TLS 握手通过 SNI 告知服务器要访问的域名,服务器返回匹配的证书。
4. Nginx 根据请求的 Host 等信息选择虚拟主机,再返回页面。

因此,“服务器 IP 能访问”不能证明域名配置正确;“IPv4 正常”也不能证明发布了 AAAA 记录的网站一定正常。排障时应该按层验证,避免同时修改多个地方。

以下 example.com 为保留示例域名,实际操作必须替换为自己持有或获授权管理的域名。所有访问检查均针对自己的服务器。

二、前置条件

准备以下资源:

• 一台可通过 SSH 管理的 Ubuntu 24.04 LTS 服务器,具备 sudo 权限。
• 一个可修改 DNS 记录的域名。
• 已明确服务器公网 IPv4;如果计划启用 IPv6,还要确认路由和入站访问均可用。
• 云厂商安全组允许实际使用的 SSH 端口,以及 TCP 80、443。
• 部署初期不要启用 CDN 代理,先让域名直接指向源站,减少排障变量。

本文使用 HTTP-01 验证申请证书,因此公网必须能通过 80 端口访问挑战文件。如果无法开放 80,应改用 DNS-01,而不是机械照搬命令。

三、步骤一:配置 DNS,不要盲目添加 AAAA

在 DNS 控制台添加:

记录类型:A
主机记录:@,部分平台要求填写完整域名
记录值:服务器公网 IPv4
TTL:部署阶段可设为 300 秒,具体以服务商允许值为准

只有确认 IPv6 可用,才添加 AAAA。AAAA 不是“高级优化开关”,错误的 IPv6 记录会让部分用户访问失败或明显变慢。

在本地检查:

dig +short A example.com
dig +short AAAA example.com
dig +short NS example.com

如果结果不符合预期,可直接向权威 DNS 查询,再与递归解析器结果比较:

dig @你的权威DNS主机名 example.com A +noall +answer

修改前已经缓存的记录,通常仍按旧 TTL 过期;现在降低 TTL,并不能立即清除外部已有缓存。不要因为本地暂时查到旧结果,就反复改动记录。

四、步骤二:安装服务并处理入站规则

安装软件:

sudo apt update
sudo apt install nginx certbot curl dnsutils
sudo systemctl enable --now nginx

如果使用 UFW,先确认 SSH 实际监听端口,并允许该端口,再启用防火墙。例如 SSH 使用默认 22 端口时:

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status verbose

只有确认规则正确后,再执行 sudo ufw enable。若 SSH 改为其他端口,必须相应替换 22。保留当前 SSH 会话,并用第二个终端验证新连接成功,避免把自己锁在外面。

UFW 和云安全组是两层独立控制:其中任意一层阻断,外部都无法访问。已有复杂防火墙或容器网络规则的服务器,不建议直接切换防火墙管理方式,应先梳理现状。

五、步骤三:先上线 HTTP,验证虚拟主机和挑战目录

创建站点目录和证书挑战目录:

sudo install -d -m 0755 /var/www/example.com/public
sudo install -d -m 0755 /var/www/letsencrypt/.well-known/acme-challenge
printf '%s\n' '<!doctype html><meta charset="utf-8"><h1>部署成功</h1>' | sudo tee /var/www/example.com/public/index.html >/dev/null

新建 /etc/nginx/sites-available/example.com,内容如下:

server {
listen 80;
server_name example.com;
root /var/www/example.com/public;
index index.html;

location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type text/plain;
try_files $uri =404;
}

location / {
try_files $uri $uri/ =404;
}
}

启用站点:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
sudo nginx -t && sudo systemctl reload nginx

如果软链接已经存在,不要重复创建,先检查它是否指向正确文件。默认站点可以暂时保留;不要在尚未盘点其他业务时,批量删除 sites-enabled 下的配置。

写入测试挑战文件:

printf '%s\n' 'acme-path-ok' | sudo tee /var/www/letsencrypt/.well-known/acme-challenge/probe >/dev/null
curl -i http://example.com/.well-known/acme-challenge/probe

建议从本地电脑或其他外部网络执行 curl,而不只是在服务器上测试。必须看到预期正文,才能说明公网 DNS、80 端口和路径映射基本贯通。

如果启用了 IPv6,在对应 server 中增加 listen [::]:80;,并分别执行 curl -4 和 curl -6 验证。仅有 IPv6 地址但没有有效入站路由,不能算部署完成。

六、步骤四:申请证书,再切换 HTTPS

申请证书:

sudo certbot certonly --webroot -w /var/www/letsencrypt -d example.com --email 你的有效邮箱 --agree-tos

示例只申请根域名,不包含 www。若需要 www.example.com,应先配置其 DNS,再额外添加 -d www.example.com,并同步调整 Nginx 的 server_name。

申请成功后,先备份原站点配置:

sudo cp -a /etc/nginx/sites-available/example.com /etc/nginx/sites-available/example.com.bak

将原文件替换为以下内容,不要与旧 server 块重复叠加:

server {
listen 80;
server_name example.com;

location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type text/plain;
try_files $uri =404;
}

location / {
return 301 https://example.com$request_uri;
}
}

server {
listen 443 ssl;
server_name example.com;

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:TLS:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

server_tokens off;
root /var/www/example.com/public;
index index.html;
autoindex off;
client_max_body_size 2m;

add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;

access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log warn;

location ~ /\. {
deny all;
}

location / {
try_files $uri $uri/ =404;
}
}

启用 IPv6 时,还需要分别在两个 server 中添加 listen [::]:80; 和 listen [::]:443 ssl;。不要只开放 IPv4 的防火墙规则。

检查并平滑加载:

sudo nginx -t && sudo systemctl reload nginx

证书路径以 Certbot 实际输出为准,已有同名证书时目录名可能带编号,可通过 sudo certbot certificates 查看。

七、步骤五:验证证书、重定向与自动续期

从外部执行:

curl -I http://example.com/
curl -I https://example.com/

预期 HTTP 返回跳转,HTTPS 返回正常状态码。不要使用 curl -k 掩盖证书问题,它会跳过证书校验。

需要绕开 DNS、直接验证某台源站时,可以使用:

curl --resolve example.com:443:你的服务器公网IPv4 -I https://example.com/

该方式仍保留正确域名和证书校验,比直接访问 https://ip 更适合定位虚拟主机问题。

为成功续期后的证书加载添加部署钩子:

sudo install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx >/dev/null <<'EOF'
#!/bin/sh
set -eu
/usr/sbin/nginx -t
/usr/bin/systemctl reload nginx
EOF
sudo chmod 0755 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx
sudo certbot renew --dry-run
systemctl list-timers --all | grep certbot

Ubuntu 仓库安装通常配有 certbot.timer,但仍应实际检查其状态,确认后可执行 sudo systemctl enable --now certbot.timer。dry-run 默认不一定执行部署钩子,应另外运行一次该脚本验证检查与 reload 链路。不要只看到证书申请成功,就默认未来续期也会成功。

八、风险与注意事项

1. 证书私钥不要复制进网站目录,不要为了“解决权限问题”改成所有人可读。Nginx 主进程会按正常权限读取私钥。
2. 本例是静态站点,不需要让 www-data 拥有目录写权限。发布账号写入、Web 服务只读,通常比让服务进程随意修改文件更稳妥。
3. 2m 请求体限制适合本例;用于上传服务或反向代理时,应根据业务重新设定,不能直接照搬。
4. 不建议上线第一天就设置长周期 HSTS、includeSubDomains 或申请 preload。应确认所有相关入口均支持 HTTPS,并理解浏览器缓存策略对回退的影响后,再逐步启用。
5. DNS 切换后不要立即销毁旧服务器。保留覆盖旧缓存存续期的过渡窗口,并观察旧服务器访问日志。
6. 日志也会耗尽磁盘。检查系统自带的 logrotate 是否覆盖 /var/log/nginx/*.log,并设置磁盘空间告警。
7. 这份配置不是 DDoS 防护方案。单机规则无法解决上游链路被打满的问题,防护应结合云平台和网络侧能力评估。

九、常见问题与排查顺序

问:域名打不开,但服务器上 curl 正常?
答:先确认测试是否真的经过公网。检查 DNS、云安全组和主机防火墙,再检查 sudo ss -lntp 中的监听地址。服务器内部访问成功不能代替外部验证。

问:部分网络正常,部分网络超时?
答:优先分别测试 curl -4 与 curl -6。如果发布了不可用的 AAAA,应修复 IPv6 或删除错误记录,再等待缓存更新。

问:证书申请失败,提示挑战文件不存在?
答:检查 A/AAAA 是否全部指向可响应挑战的服务器,挑战路径是否被其他配置拦截,以及域名是否设置了限制证书机构的 CAA 记录。不要通过关闭证书验证解决问题。

问:修改配置后网站仍是旧页面?
答:检查 nginx -t 是否成功、reload 是否成功,以及是否命中了其他虚拟主机。sudo nginx -T 可以展示完整生效配置,但输出可能含敏感配置,不应原样公开。

问:HTTPS 配置失败如何回退?
答:恢复此前备份的纯 HTTP 配置,再执行 nginx -t 与 reload,先恢复基本可用性。不要反复重启服务碰运气,也不要删除证书目录。若已长期启用 HSTS,纯 HTTP 回退对相关浏览器可能无效,这也是早期谨慎启用 HSTS 的原因。

十、总结

可靠上线不是“装好 Nginx,再申请一个证书”,而是让每一层都能单独验证:DNS 返回预期地址,公网端口确实可达,虚拟主机匹配正确,证书校验通过,续期后能重新加载,变更失败时有明确回退路径。

我的建议是把验收条件写进部署记录:外部 HTTP 挑战路径正常、HTTPS 不跳过校验即可访问、已发布的 IPv4/IPv6 均通过检查、续期演练成功、防火墙不会阻断管理入口。相比堆叠大量未经验证的“优化参数”,这些基础检查更能减少真实运维中的故障。
 
顶部