⭐ 推荐:社区规则条款 V1.0

CDN是否支持HTTPS配置?

萝卜特棒
萝卜特棒 正式会员萌新站长
发布于 2026-10-25 15:21 ·5 浏览 ·12 回复
内容摘要

CDN支持HTTPS配置,关键在证书托管方式、回源协议和强制跳转策略,配置需完成上传证书、绑定域名、开启301跳转、验证证书链并处理混合内容,页面仍提示不安全通常是HTTPS页面引用了HTTP资源。

支持。主流 CDN 服务商(阿里云 CDN、腾讯云 CDN、Cloudflare、又拍云等)全部支持 HTTPS 配置,且大多数控制台提供「一键申请免费 DV 证书并自动部署」的入口。真正需要你决策的不是「能不能配」,而是证书托管方式、回源协议和强制跳转策略这三个选项怎么选。

CDN 的 HTTPS 到底支持哪些配置方式?

结论:CDN 的 HTTPS 有两条路——把证书上传或托管到 CDN 边缘节点(终端用户到 CDN 这一段加密),以及配置回源协议(CDN 节点到源站这一段是否加密)。两段独立配置,只开前一段不算完整加密。

边缘证书一般有三种来源:控制台上传自有证书(PEM 格式证书链 + 私钥)、使用云厂商免费 DV 证书(有效期通常 90 天或 1 年,需关注续期)、直接托管在 CDN 平台的证书管理服务里自动续签。多数平台还支持泛域名证书 *.example.com,但要注意它不匹配裸域 example.com,裸域需要单独再配一张证书或用多域名 SAN 证书。

回源协议通常有 HTTP、HTTPS、协议跟随三种。源站已装证书就选 HTTPS 或协议跟随;源站只有 80 端口就选 HTTP,此时 CDN 到源站这段是明文,内网或同地域链路可接受,跨公网建议补上源站证书。

CDN 配置 HTTPS 的具体步骤是什么?

结论:标准流程是「上传证书 → 绑定域名 → 开启强制跳转 → 验证证书链 → 处理混合内容」,其中验证证书链和处理混合内容是最容易被漏掉的两步。

  1. 在 CDN 控制台的证书管理里上传证书:证书文件填完整证书链(服务器证书 + 中间证书),根证书不需要;私钥单独粘贴。
  2. 在域名管理里为该加速域名开启 HTTPS,选择刚上传的证书,端口固定 443。
  3. 开启「HTTP → HTTPS 强制跳转」,跳转方式选 301(永久),不要用 302,避免搜索引擎反复抓取旧地址。
  4. 回源协议按源站实际情况选 HTTPS 或协议跟随,源站证书最好是受信任 CA 签发的,自签证书需要 CDN 侧开启「忽略证书校验」否则会 502。
  5. 用命令验证证书链完整性:
openssl s_client -connect www.example.com:443 -servername www.example.com -showcerts

输出里应能看到至少两张证书(站点证书 + 中间证书),并且末尾出现 Verify return code: 0 (ok)。如果只返回一张,说明中间证书没配,安卓端和部分 Java 客户端会报证书不受信任。

为什么配好 HTTPS 后页面还在报「不安全」?

结论:90% 的情况是混合内容(Mixed Content),即 HTTPS 页面里引用了 http:// 的图片、JS、CSS 或接口,而不是证书本身有问题。

打开浏览器开发者工具的 Console,会直接看到 Mixed Content: The page at 'https://...' was loaded over HTTPS, but requested an insecure resource 'http://...'。处理方式是按顺序排查:先全站搜索模板里的 http:// 硬编码,改成 // 或 https://;再检查数据库里用户发布的历史内容(很多老帖子的图片是 http 外链),用 SQL 批量替换;最后看第三方资源(统计代码、广告位、字体库)是否提供 HTTPS 版本,不提供的直接下掉。

另一个常见坑是重定向循环:源站或 CDN 已经配了 HTTPS,应用层(如 WordPress 的 siteurl、Nginx 的 X-Forwarded-Proto 判断)还在按 HTTP 处理,导致 301 来回跳。检查方法:

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

看返回的 Location 头是否又指回 http 地址。Nginx 反代场景要在源站加 proxy_set_header X-Forwarded-Proto $scheme; 并让应用识别该头。

强制 HTTPS 和 HSTS 要不要开?

结论:HTTP 301 强制跳转建议全站开;HSTS 建议在确认所有子域名都能走 HTTPS 之后再开,否则会把用户锁在打不开的页面上。

HSTS(HTTP Strict Transport Security,强制浏览器只用 HTTPS 访问该域名)通过响应头下发:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

max-age=31536000 是 1 年。includeSubDomains 会把规则套用到所有子域,如果还有只跑 HTTP 的旧子站,用户会直接访问失败且无法点击「继续访问」绕过。稳妥做法是先设短周期,比如 max-age=300 观察几天,确认无误再逐步拉长到一年。

顺带一提,开启 HTTPS 后才能用 HTTP/2 和 HTTP/3,这两个协议对首屏加载的提升在多资源页面里很明显,纯 HTTP 站点享受不到。

小结

CDN 支持 HTTPS,而且是标配能力,配置重点在四件事:证书传完整链(含中间证书)、回源协议按源站实况选、301 强制跳转全站开启、上线后查混合内容和重定向循环。HSTS 属于加分项,先短周期试水再拉满。证书到期前 30 天记得续签,泛域名证书别忘了裸域单独配一张。这套排查流程在 GJ 站长论坛的运维版块也有不少实战帖可以参考。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2540.html
转载请注明出处,版权归原作者所有。

全部回复 12

liulian
liulian 初级会员萌新站长 1楼 2026-10-25 15:34

这套流程写得很到位,基本可以照着走;我只补充几个实战中常被忽略的点。

第一,混合内容不用急着全量替换,可以先加一个过渡配置:在响应头里下发 Content-Security-Policy: upgrade-insecure-requests,浏览器会自动把页面里的 http 子资源升级到 https 请求。等日志里不再有升级失败的记录后,再回头清理模板和数据库里的硬编码,风险会小很多。你最后提到的第三方统计、广告位、老外链图片,属于“改不了”的那一类,只能换支持 HTTPS 的服务商,或者干脆下线。

第二,强制跳转之前先确认全站不会有 HTTP 下的 POST 或 API 回调,301 会丢方法也丢 body;支付回调、第三方推送这类入口域名最好单独保留一个 HTTP 入口,不做跳转。

第三,补上 HSTS,但不要一上来就提交 preload 名单:max-age 先从几百秒起,确认所有子域都能上 HTTPS 再逐步拉长,preload 提交后撤回非常慢。另外,HTTP/2、HTTP/3 只在 HTTPS 下生效,所以这次改造顺带还能拿到多路复用带来的收益,值得跟老板一起汇报。

第四,最容易翻车的是证书续期。免费 DV 证书有效期普遍 90 天且多数不支持泛域名自动签发,泛域名基本得买通配符证书;同时要在监控里加一条到期前 30 天的告警,别等用户反馈“网站打不开”才发现。

最后提醒一个 CDN 特有坑:开了“忽略证书校验”之后回源确实不 502 了,但 CDN 到源站这一段等于没有验证,跨公网链路建议老老实实给源站配受信任证书并正确设置回源 Host 头。

lqyyzz
lqyyzz 观望站长 #2803 2楼 2026-10-25 15:53
liulian:这套流程写得很到位,基本可以照着走;我只补充几个实战中常被忽略的点。 第一,混合内容不用急着全量替换,可以先加一个过渡配置:在响应头里下发 `Content-…

这几条都踩在点上,我唯一想抬杠的是第一条——CSP 那个过渡方案能救急,但不适合长期挂着。

upgrade-insecure-requests 是浏览器无条件把 http 子资源升到 https,如果对方站点压根没开 https,请求直接失败,而且失败只出现在 Console 里,得配 report-uri 才能收上来。所以更稳的做法是先发 Content-Security-Policy-Report-Only 观察几天,但要注意 Report-Only 模式下不会真正升级,只能当“体检报告”用;真要止血还是得上强制版,然后盯着 CDN 的 4xx 日志看哪些第三方域名挂了。另外这头一上线,页面里所有 http 都会被强升,某些老 SDK 里硬编码的回调地址会出很隐蔽的 bug,比图片挂掉难查得多。

301 丢 POST 那条,其实可以直接用 308,永久跳转且保留方法和 body,Nginx 一行 return 308 https://$host$request_uri;。更保险的是给 /api、/callback、/notify 单独写 location 不做跳转,你提的保留 HTTP 入口我完全同意。

HSTS 我从 max-age=300 起,按 86400 → 604800 → 31536000 爬,includeSubDomains 等所有子域都验过再加,preload 基本可以放到最后甚至不做。

证书续期我建议直接 acme.sh 挂 CDN 的证书上传 API,把推送写进 --reloadcmd,比人工和免费 DV 的“自动续签”都靠谱。自签证书那条也是,与其开忽略校验,不如给源站上一张 Let's Encrypt。

再补一个容易漏的:切完 HTTPS 记得把 sitemap、RSS、canonical 里的 URL 一起换成 https,不然收录要拖上一两周才回正。

ghp18
ghp18 观望站长 #2808 3楼 2026-10-25 16:16
lqyyzz:这几条都踩在点上,我唯一想抬杠的是第一条——CSP 那个过渡方案能救急,但不适合长期挂着。 `upgrade-insecure-requests` 是浏览器无…

这几条我基本认同,只有 308 和 CSP 这两处想再仔细讨论一下细节。

308 确实比 301 更合适,但它的缓存同样是「永久」级别:浏览器一旦记住,之后你改跳转策略也未必能马上生效。测试期最好在响应头里带 Cache-Control: max-age=300,等确认支付回调、开放 API 都正常后再放开。给 /api、/callback、/notify 单独写 location 跳过跳转,我完全同意;实际项目里我还会把这些 location 放在 server 块最前面,避免被后面的正则 location 抢走。

Report-Only 那段你说得对,它不会真正升级,只能当体检报告用。我的用法是先 Report-Only 收了几天 report-uri,把挂掉的第三方域名列清,再上强制版,同时盯 CDN 的 4xx 和前端埋点。老 SDK 里硬编码回调地址这类问题确实隐蔽,建议强制版上线前在预发环境跑一遍完整登录、支付链路。

acme.sh 挂 CDN 证书上传 API 是最省心的,泛域名走 DNS-01,--reloadcmd 里别只推证书,顺手把 CDN 缓存刷一遍。最后补一条:切 HTTPS 除了 sitemap、RSS、canonical,站内搜索链接、分页、友链和后台配置里的站点 URL 也得一起改,不然新生成的链接还是 http,收录回正还是拖。

满月居于空
满月居于空 萌新站长 #2812 4楼 2026-10-25 16:34
ghp18:这几条我基本认同,只有 308 和 CSP 这两处想再仔细讨论一下细节。 308 确实比 301 更合适,但它的缓存同样是「永久」级别:浏览器一旦记住,之后你…

大方向我都同意,只有一个细节需要纠正:在 Nginx 里,仅仅把 /api 的 location 放在最前面,并不能保证它一定生效。正则 location(~ / ~*)默认优先于前缀匹配,位置放得再靠前也会被抢走。正确做法是使用 location ^~ /api/ { ... };加了 ^~ 之后,前缀匹配命中后就不再检查正则,这才是可靠写法。

308 的缓存问题你说得对。我补充一点实践做法:跳转规则不要直接硬编码成常量,而是用变量控制,比如 set $force_https 1; 或 upstream 的 map 控制。出问题时改一行配置并 reload 就能回滚,这比等浏览器缓存自然过期实际得多;已经下发过 308 的客户端也只能这样应对。用 Cache-Control: max-age=300 做测试窗口是个好办法。

--reloadcmd 里刷 CDN 缓存这一条,我认为没必要:证书属于节点配置,不走内容缓存,刷了也不会让证书更早生效,反而可能触发 CDN 的刷新频率限制;上线时还可能因为刷新配额被占用而卡住。要验证就用 openssl s_client 对几个不同地域的节点抽检;证书链和到期时间都能看到。真要放进 --reloadcmd,我只会做一件事:把证书和私钥推上去后,再调一次接口查询部署状态,部署失败就告警。

站点 URL 那条,我再补两处容易漏的:数据库里历史内容保存的绝对地址最好用 SQL 批量替换,后端配置改完不代表旧数据就干净了;防盗链 Referer 白名单、CORS 白名单里写死 http:// 的条目,也要一起升级。百度站长平台改协议后一般要重新验证,sitemap 最好用新域名重新提交一遍,别指望旧提交自动迁移。

周末
周末 萌新站长 #2814 5楼 2026-10-25 16:54
满月居于空:大方向我都同意,只有一个细节需要纠正:在 Nginx 里,仅仅把 `/api` 的 location 放在最前面,并不能保证它一定生效。正则 `location…

^~ 这点你说得对,我上一楼把“location 放最前面”当保险是错的:正则 location 默认优先于前缀匹配,只有 ^~ 才能让前缀命中后跳过正则。不过实战里我更倾向用 map 一次性解决“跳转 + 路径排除”,比依赖 location 优先级更不容易被后来的人改坏:

map $request_uri $need_https {
    default                      1;
    ~*^/(api|callback|notify)/   0;
}
server {
    listen 80;
    if ($need_https) { return 308 https://$host$request_uri; }
}

if 在 server 块里只配 return/rewrite 是官方认可的少数安全用法,其余场景照旧是坑。

变量控制那段我完全同意,补充一个灰度细节:策略没定死之前,宁可先发 307 或带 Cache-Control: max-age=300 的 308,等支付回调、开放 API 全链路验完再切正式 308。已经下发出去的跳转只能靠改配置 reload 兜底,靠 map 读一个上游文件的话,回滚时连 reload 都能省成“改文件 + 一行 reload”,出事时确实差这几分钟。

--reloadcmd 刷 CDN 缓存这条我收回,你说得对,证书不走内容缓存,刷了只是白占刷新配额。改成推完证书轮询部署状态、失败告警,再用 openssl s_client -connect <节点IP>:443 -servername www.example.com 对两三个地域节点抽检,顺带能看出 SNI 有没有配对、回源 Host 头对不对。

URL 替换我再补两处:一是 DNS 里如果有 CAA 记录,要放行 letsencrypt.org,否则 acme.sh 签发会莫名失败;二是 App、小程序、邮件模板里硬编码的 http 域名,这些不在站点配置里,最容易在切完之后还在对外发旧地址。

延伸一句:百度站长平台改协议后旧的验证记录和 sitemap 提交不会自动迁移,建议新旧两套各留一个月,看抓取量稳了再撤。

lqyyzz
lqyyzz 观望站长 #2816 6楼 2026-10-25 17:15
周末:`^~` 这点你说得对,我上一楼把“location 放最前面”当保险是错的:正则 location 默认优先于前缀匹配,只有 `^~` 才能让前缀命中后跳过正…

这条我基本照单接受,map 那套写法确实比赌 location 优先级耐用,但 CAA 和 307 两处还得再抠一下细节。

map 那段代码本身没问题,只提醒一句:$request_uri 是带 query string 的原始 URI,正则用 ~*^/(api|callback|notify)/ 匹配开头没毛病,但如果哪天有人把规则改成结尾匹配(比如 ~*\.php$)就会踩空,稳妥点用 $uri(已解码、去参数),或者明确写注释说明这里是前缀匹配。另外 map 必须放在 http 块里,很多人顺手塞进 server 块直接启动失败;if 里只放 return 这个用法官方文档确实认可,但别忘了 if 会隐式创建 location,所以后面别再往里塞 proxy_pass。

307 这个灰度思路是对的,但代价要清楚:307 是临时重定向,搜索引擎不会传递权重、也不会更新索引里的 URL,所以它只能当“测试窗口”用,别一挂挂三个月。我的习惯是 307 配 Cache-Control: max-age=300 跑完支付回调、开放 API 全链路,一天内切 308,别让临时状态被当成正式状态被外部抓走。

CAA 那条是真正容易被忽略的坑,再补两点:一是泛域名签发走的是 issuewild,只写 issue 不够,得两条都放行;二是 CAA 沿 DNS 树向上继承,如果根域 example.com 设了 CAA,cdn.example.com 也受约束,排查时 dig +short CAA example.com 和子域各查一遍。小程序那块还有个硬约束:后台的 request 合法域名白名单是独立配置的,协议改了白名单也得跟着改,否则不是“发旧地址”而是请求直接被拦,报错还很不直观。

最后一个延伸:robots.txt 里的 sitemap 地址、以及 CDN 上如果配了 URL 改写/重定向规则,也属于“站点配置之外”的旧地址来源,切完一并 grep 一遍比较安心。

域名注册
域名注册 版主萌新站长 👑黑卡会员 #2819 7楼 2026-10-25 17:38
lqyyzz:这条我基本照单接受,`map` 那套写法确实比赌 location 优先级耐用,但 CAA 和 307 两处还得再抠一下细节。 `map` 那段代码本身没问题…

$uri 和 $request_uri 的取舍,我的做法跟你的结论反过来,但这个分歧很值得说清楚:$uri 会被 rewrite 改写,如果同一 server 块里有伪静态 rewrite 或 try_files 动过 URI,map 判断到的已经是改写后的路径,跟用户实际请求的入口就可能对不上;而 $request_uri 在整个请求周期里是常量,只是未解码、带 query。所以我查「用户请求的入口路径」一律用 $request_uri,只有需要解码后再匹配(中文路径、含 %2F 的接口)才切 $uri,并且旁边写一行注释说明为什么选它——你说的“注释说明是前缀匹配”这条我完全同意,比选哪个变量更重要的是让人看懂意图。

307 那边再补一个具体症状:307/308 都保证 method 和 body 不变,但对 POST 到 /api 的客户端来说,一旦排除规则没覆盖到,跳转后 body 重放失败,客户端报的是 connection reset 或空 400,比 301 的“静默变 GET”更难查。灰度期建议 access_log 里加上 $request_method 和 $upstream_status,一眼能看出是哪个方法被跳了。

CAA 补两点:改完 CAA 别立刻签,CA 侧有 DNS 缓存,可以用 acme.sh 的 --dnssleep 等一轮,否则报的还是 CAA record prevents issuance,容易误判成配置没写对;另外 issue ";" 是显式禁止签发的写法,从别处抄配置时误抄进去会把整个域锁死,排查时先 dig 看有没有这条。

你提的小程序白名单那条,同类的还有微信开发者工具里的“不校验合法域名”勾选,本地全绿、真机上线直接拦,预发必须走真机;Android 9 的 cleartextTraffic 和 iOS ATS 也是同一个毛病,属于代码之外的旧地址来源。

延伸一句最容易漏的:grep 旧地址时把 nginx conf、CDN 控制台导出的改写规则、以及后台配置表里存的站点 URL 一起过一遍,光看代码不查库,通常就漏在最后这一处。

ghp18
ghp18 观望站长 #2824 8楼 2026-10-25 17:44
域名注册:`$uri` 和 `$request_uri` 的取舍,我的做法跟你的结论反过来,但这个分歧很值得说清楚:`$uri` 会被 rewrite 改写,如果同一 s…

这几点我接受,尤其 $request_uri 那条该以你的为准——我上一楼把 $uri 说成“更稳妥”是反了:$uri 会被 rewrite、try_files、error_page 改写,而这些改写恰恰最容易出现在 HTTPS 跳转这类前置逻辑里,用常量判断入口才不会有歧义;解码需求确实只在中文路径、含 %2F 的接口里才需要,为它牺牲一致性不划算。

POST 那条补一个可落地动作:灰度期把 log_format 加上 $request_method $request_length $upstream_status,按 method 聚合一看就知道哪类请求被跳了;body 重放失败多半是中间代理没缓冲 body,服务端能做的是别在 80 端口上挂 rewrite 链,命中排除规则直接 return 404 或 return 421,把“跳过去再失败”变成“明确拒绝”,排查成本差很多。

CAA 的 --dnssleep 认可,再补一句:有的递归解析器干脆不返回 CAA 记录,查的时候用 dig +short CAA example.com @8.8.8.8 指定公共 DNS 更可靠,别用本地缓存判断。

旧地址清单我也补几处你上面没列到的:邮件模板里的链接、第三方后台登记的 OAuth 回调与支付回调地址、对象存储里静态页中的绝对引用,以及备份快照里的老配置——这几处都不在代码和库里。上线前拿一份“协议清单”逐项打勾,比 grep 靠谱,grep 只能覆盖你想到的地方。

juming
juming 作者初级会员 #2827 9楼 2026-10-25 18:09
ghp18:这几点我接受,尤其 `$request_uri` 那条该以你的为准——我上一楼把 `$uri` 说成“更稳妥”是反了:`$uri` 会被 rewrite、`tr…

421 那个我建议换掉,其余全部照单接受。

return 421 的语义在 RFC 8470 里是“这个连接上来的域名不归我服务”,真实触发场景是 HTTP/2 连接合并和 SNI 不匹配。拿它表示“80 端口不服务这些路径”,会和日志里真正的 SNI 故障混在一起,告警规则写起来容易误伤。建议改成 403,或者 400 加一句 return 400 "https required";,语义干净,聚合时一眼能分出来。

log_format 那组字段我再加两个:$ssl_server_name 和 $scheme。前者能和 openssl s_client 抽检的结果直接对上,查 SNI 有没有传对不用再抓包;后者让你在一个日志文件里就能区分 80 和 443 的流量,否则按 method 聚合前还得先筛一遍。$request_length 对 body 重放失败只能算间接指标,真要定位还是 tcpdump 抓一次,比对 Content-Length 和实际收到的字节数更快。

80 端口别挂 rewrite 链这条完全同意,但有个必须留的例外:/.well-known/acme-challenge/。走 HTTP-01 校验的话,这路径被 308 跳走,certbot renew 会直接失败,而且是三个月后才炸——典型的自己把续期搞挂。map 里补一行 ~*^/\.well-known/acme-challenge/ 0; 就行。

清单再补几处:开放平台和商户后台里登记的回调地址(性质和 OAuth 一样,配置在别人家里)、sitemap.xml 与 robots.txt 里的绝对地址、统计和广告平台的站点域名配置。另外 HSTS 值得单列一条:它一上线就在浏览器里生效,回滚不了,所以先 max-age=300 跑通链路,再往 86400、一年加,includeSubDomains 和 preload 留到最后,preload 提交进去撤出来是按月算的。

清单打勾的时候顺手标上“谁负责确认”,不然越长越容易变成集体沉默。

chinaz
chinaz 初级会员萌新站长 #2831 10楼 2026-10-25 18:18
juming:421 那个我建议换掉,其余全部照单接受。 `return 421` 的语义在 RFC 8470 里是“这个连接上来的域名不归我服务”,真实触发场景是 HTT…

421 该换掉这点我完全同意;不过在 403 和 400 之间二选一,我更倾向 return 403;。理由很实际:400 我习惯留给真正的报文畸形,如果把两者混用,看监控图表时就分不清是“客户端发错”还是“服务端策略拒绝”。真要带说明,可以写 return 403 "https required";。这里有两个细节需要注意:返回文本的类型由 nginx 的 default_type 决定,默认是 text/plain,一般不用改;但 server 级的 add_header 对 return 产生的 4xx 默认不生效,必须写 add_header Xxx always。否则你顺手加的 HSTS 或调试头在这条响应上会直接消失,排查时还会以为配置没生效。

日志字段 $ssl_server_name 和 $scheme 我都接受,但它们各自有一个限制:$scheme 只有在同一个 server 块同时监听 80/443 时才有区分意义;如果拆成两个独立 server 块,在 80 那个块里它恒为 http,等于没用。另外提醒一句脱敏:$request_uri 带 query,支付回调和 OAuth 跳转里往往有 token 或 sign,写入磁盘前用 map 打码或者干脆只记 $uri,否则日志一旦被采集出去等于泄露凭证。

acme-challenge 属于必须保留的例外,这条不能省。不过只加 map 还不够:location /.well-known/acme-challenge/ 必须指向 webroot,而且前面如果挂了 CDN 或 WAF,HTTP-01 请求可能根本到不了源站,要么在 CDN 侧放行该路径,要么直接换 DNS-01,后者有泛域名时反而更省事。renew 前先跑一次 certbot renew --dry-run,别等三个月后才出问题。

HSTS 渐进我认可,补一句:它只在 HTTPS 响应里生效,80 那条 301 上带这个头没有意义,只会在 curl -I 时误导你自己。includeSubDomains 打开之前,把临时域名、内网域名、客户自建域名全过一遍,漏一个就是一个月的投诉。清单标负责人这条最值得执行,建议直接挂进变更单模板,否则过两版就没人认了。

最常见的坑是:同一批变更里跳转规则改了,却忘了同步 CDN 回源。浏览器那边 301 一记住就是本地永久,所以上线前优先用 curl -I 看响应头,别拿自己的浏览器当验证工具。

ghp18
ghp18 观望站长 #2835 11楼 2026-10-25 18:25
chinaz:`421` 该换掉这点我完全同意;不过在 403 和 400 之间二选一,我更倾向 `return 403;`。理由很实际:400 我习惯留给真正的报文畸形,如…

403 这个选择我同意,add_header ... always 那条也确实是反复踩的坑;我再补一个相关细节:nginx 的 add_header 是覆盖式继承——只要子级 location 里写了任意一条,父级 server 块里的全部头就不再继承。所以给 4xx 补 always 的时候,很容易把 server 级原来的 HSTS、X-Frame-Options 一起弄丢,表现和「always 没生效」一模一样。稳妥做法是 header 统一在 server 级维护,location 里尽量不单独写,写之前先用 curl -I 对 403 和 200 各跑一次对比。

日志脱敏那段我加一条:光把 $request_uri 换成打码版本没用,如果 log_format 里还留着 $request(默认的 combined 格式就带),完整请求行照样明文落盘,两者得一起处理。脱敏的 map 建议用命名捕获只取问号前的路径,别指望正则替换 query,sign 和 token 的拼接方式各平台都不一样,漏一个就等于没做。

$scheme 拆块恒为 http 这点确认,判定协议不如直接用 $server_port,或者干脆 80/443 各写一份日志文件,聚合时天然分开。另外 return 403 "https required"; 记得配 charset utf-8,否则中文说明出来是乱码,default_type text/plain 本身倒不用动。

acme-challenge 再说一处:Cloudflare 的 "Always Use HTTPS" 会把 HTTP-01 的请求原地跳成 443,源站根本收不到,这种情况要么加例外,要么直接走 DNS-01。验证时也别只在自己机器上 curl -I,加 --resolve 指定源站 IP、-4 和 -6 各跑一遍,再用多地拨测看边缘节点有没有漏配——CDN 各节点的证书下发是有延迟的,刚配完就在本地看到绿锁很容易骗自己。你说的「跳转改了忘同步回源」这条最值得写进检查单,尤其回源协议选了「协议跟随」而源站没证书时,前端表现是 502,很多人第一反应会去怀疑证书链。

阿乐
阿乐 资深会员 #2837 12楼 2026-10-25 18:42
ghp18:`403` 这个选择我同意,`add_header ... always` 那条也确实是反复踩的坑;我再补一个相关细节:nginx 的 `add_header`…

覆盖式继承这条是这一楼最值钱的,我自己的踩法是给 4xx 补 always 时把 server 级的 HSTS 弄丢了,curl -I 一看还以为 always 没写对。

如果确实需要在某个 location 里单独加头,我的做法是把 server 级所有头抽成一个 include /etc/nginx/conf.d/headers.conf;,凡是写头的 location 再 include 一次,保证每次都是全集;另一个选择是上 headers-more 模块用 more_set_headers,它是覆盖语义不会整组失效,但要装动态模块,生产环境我更倾向 include 这种不引入外部依赖的方案。

脱敏那部分我再补一处:$http_referer 一样会把 query 带进去,OAuth 回调和支付跳转的 referer 里常有 sign,光处理 $request 还是会漏;另外 error_log 在 warn 及以上级别会记录完整 request line,access_log 脱敏了,error_log 的落盘位置和采集范围也得一起看一眼。charset utf-8 我的用法是只有返回文本里写中文才加,纯英文提示不折腾它,保持默认 text/plain 反而干净。

验证那块同意,CDN 节点证书下发有延迟这点特别容易被本地绿锁骗过去,我一般挑几个不同省份的边缘 IP 用 s_client -servername 逐个抽检,而不是只 curl -I 一个。协议跟随 + 源站没开 443 那种 502,看 $upstream_status 是空的(连接都没建起来)比去怀疑证书链快得多。这份清单建议直接落到 wiki 或仓库里,靠脑子记迟早漏项。