acme.sh + 阿里云DNS实现泛域名SSL证书全自动申请与续期
1. 项目概述与核心价值最近在折腾自己的几个小项目发现SSL证书管理是个不大不小的麻烦事。尤其是那些带子域名的站点每次申请、部署、续期都得手动操作一遍费时费力不说还容易忘。直到我发现了acme.sh这个神器配合阿里云的DNS API终于实现了全自动化的泛域名SSL证书申请与续期。这套方案在Debian服务器上跑用Nginx做Web服务器可以说是个人开发者和小团队的福音。简单来说这个方案能帮你做什么它能让你用一行命令为像*.yourdomain.com这样的泛域名自动从Let‘s Encrypt申请到免费的SSL证书并且自动完成Nginx的配置更新。最关键的是证书90天到期前它会自动帮你续期整个过程完全无人值守你再也不用惦记着证书过期这档子事了。无论你是运维新手还是想优化现有流程的老手这套基于acme.sh的方案都值得一试。它把复杂的证书生命周期管理简化成了几个清晰的步骤稳定性经过了我多个生产环境的长期考验。2. 方案选型与工具解析为什么选择acme.sh 阿里云DNS Nginx这个组合这里面的每一个选择都有其背后的考量。市面上实现ACME协议自动化证书管理环境的工具不少比如Certbot、lego等。acme.sh的优势在于它纯粹用Shell脚本写成依赖极少几乎可以在任何Unix-like系统上运行特别适合嵌入到各种自动化流程或资源受限的环境中。它的设计哲学是“零依赖”安装后就是一个独立的脚本管理起来非常清爽。而选择阿里云的DNS API进行域名验证是泛域名证书申请的关键。Let‘s Encrypt验证域名所有权主要有几种方式HTTP-01在网站根目录放文件、DNS-01在域名DNS解析里添加TXT记录。对于泛域名证书*.domain.comHTTP-01验证方式是无法使用的因为你不可能在每一个可能的子域名下都放置验证文件。因此DNS-01是唯一的选择。这就需要你的域名DNS服务商提供API允许通过脚本自动添加和删除TXT记录。阿里云DNS的API功能完善、文档清晰且对于持有阿里云域名的用户来说集成最方便成为了我的首选。至于服务器系统选DebianWeb服务器选Nginx这更多是基于个人技术栈和生态的考虑。Debian/Ubuntu系列在服务器领域占有率高软件包管理成熟稳定Nginx则以高性能和灵活的配置著称。acme.sh对这两者的支持都非常好有现成的脚本可以自动将新证书安装到Nginx的配置目录并重载服务。整个技术栈搭配起来形成了一个闭环的自动化解决方案。注意使用DNS-01验证方式意味着acme.sh需要拥有你域名DNS的修改权限通过API密钥。请务必妥善保管API密钥并遵循最小权限原则只授予添加/删除TXT记录的权限。3. 环境准备与依赖安装在开始自动化之旅前我们需要先把舞台搭好。这套方案的核心是acme.sh脚本它需要运行在一个可以访问互联网并且安装了基础工具的Linux环境里。以下步骤以Debian 11/12为例其他发行版可能略有不同。3.1 系统基础环境检查首先确保你的服务器系统是最新的并且安装了curl和cron用于定时任务。通过SSH登录你的Debian服务器执行以下命令sudo apt update sudo apt upgrade -y sudo apt install curl cron -y这两条命令会更新软件包列表升级现有软件并安装我们必需的curl用于下载脚本和cronacme.sh依赖它来设置自动续期任务。通常这些在干净的Debian系统中都是预装的但检查一下总没错。3.2 获取并安装acme.shacme.sh的安装极其简单。它推荐的方式是直接通过curl下载脚本并安装到用户目录下避免污染系统目录。curl https://get.acme.sh | sh -s emailmyexample.com将myexample.com替换为你自己的邮箱地址。这个邮箱用于接收证书相关的通知比如续期失败告警非常重要。执行这条命令后脚本会自动完成以下工作将acme.sh安装到~/.acme.sh/目录下。为你创建一个bash的alias方便使用alias acme.sh~/.acme.sh/acme.sh。自动为你创建一个cronjob定时任务用于每天检查证书是否需要续期。安装完成后你需要重新加载一下shell配置或者新开一个终端窗口让alias生效。也可以直接使用绝对路径~/.acme.sh/acme.sh来执行命令。实操心得安装过程可能会因为网络问题失败。如果curl下载缓慢可以尝试多执行几次或者先手动下载脚本再执行。安装成功后务必检查~/.acme.sh/目录是否存在并且acme.sh脚本有可执行权限。3.3 配置阿里云DNS API密钥这是整个流程中最需要谨慎的一步。我们需要在阿里云控制台创建一个具有DNS管理权限的RAM子用户并使用其AccessKey。登录阿里云控制台访问阿里云官网并登录。进入RAM访问控制在控制台首页搜索“RAM”或直接访问访问控制RAM服务。创建用户在“用户”页面点击“创建用户”。填写用户名例如acme-dns并务必勾选“控制台密码登录”和“编程访问”后者用于生成API密钥。点击确定。授权策略为用户授权。在用户列表找到刚创建的用户点击“添加权限”。在系统策略中搜索并选择AliyunDNSFullAccess管理云解析DNS的权限。为了安全强烈建议只授予这个权限遵循最小权限原则。获取AccessKey创建成功后在用户详情页的“认证管理”选项卡下你会看到“创建AccessKey”的选项。点击创建并务必立即保存弹出的AccessKey ID和AccessKey Secret。这个Secret只显示一次关闭后就无法再次查看。拿到Key之后我们需要将其作为环境变量导出供acme.sh使用。回到服务器终端执行export Ali_Key你的AccessKey ID export Ali_Secret你的AccessKey Secret重要警告上述命令将密钥暴露在命令行历史和环境变量中有一定风险。更安全的做法是将这两个变量写入你的shell配置文件如~/.bashrc或~/.profile中然后source一下。或者acme.sh也支持从文件中读取你可以将密钥保存到~/.acme.sh/account.conf中格式为SAVED_Ali_Keyxxx和SAVED_Ali_Secretxxx。选择一种你熟悉且安全的方式。4. 申请泛域名SSL证书实操环境配置妥当密钥也已就位现在可以开始申请我们的第一张泛域名证书了。这个过程是自动化的核心体现。4.1 执行证书签发命令假设你的域名是example.com你想要一张覆盖*.example.com的证书。使用以下命令~/.acme.sh/acme.sh --issue --dns dns_ali -d *.example.com -d example.com让我们拆解一下这个命令--issue表示执行签发证书的操作。--dns dns_ali指定使用DNS-01验证方式并且使用阿里云dns_ali的DNS API。acme.sh支持几十种DNS服务商dns_ali是其中之一。-d *.example.com指定主域名泛域名写法。这是证书的主体。-d example.com同时申请根域名的证书。这是一个好习惯因为有些场景下浏览器可能直接访问根域名。Let‘s Encrypt允许一张证书包含多个域名SAN证书。执行命令后acme.sh会开始它的工作流向Let‘s Encrypt的ACME服务器发起证书申请。Let‘s Encrypt返回一个挑战challenge要求你在_acme-challenge.example.com这个子域名下设置一个特定的TXT记录值。acme.sh自动调用阿里云DNS API为你添加这条TXT记录。等待DNS记录全球同步脚本会多次查询确认。验证通过后acme.sh再次调用API清理掉刚才添加的TXT记录。最后从Let‘s Encrypt服务器获取签发的证书和密钥。整个过程完全自动化你只需要等待1-2分钟即可。如果一切顺利你会在最后看到“Cert success.”之类的成功信息。证书和密钥文件会被保存在~/.acme.sh/*.example.com/目录下。4.2 证书文件解析与验证申请成功后进入证书目录查看一下ls -la ~/.acme.sh/*.example.com/你会看到几个关键文件*.example.com.cer或fullchain.cer这是证书链文件包含你的域名证书和中间CA证书。Nginx配置中通常使用这个。*.example.com.key这是你的私钥文件必须严格保密。ca.cer中间CA证书。*.example.com.confacme.sh为该域名保存的配置信息。你可以使用openssl命令来验证证书的详细信息比如查看有效期和包含的域名openssl x509 -in ~/.acme.sh/*.example.com/fullchain.cer -noout -text | grep -A1 -B1 Subject:\|Not After\|DNS:这个命令会输出证书的主题域名、过期时间以及所有支持的域名SAN列表确认泛域名*.example.com和example.com都在其中。注意事项首次申请可能会因为DNS传播延迟而失败。如果遇到“Invalid domain”或验证超时错误别着急。可以尝试在命令后加上--debug参数查看详细日志或者手动去阿里云控制台检查TXT记录是否已添加成功。有时需要等待更长一点时间如5-10分钟让DNS生效特别是某些地区的DNS缓存较慢。5. 自动化安装与Nginx配置证书申请下来只是第一步让Nginx用上它并实现自动更新才是“免维护”的关键。acme.sh提供了一个非常强大的--install-cert命令。5.1 配置证书自动安装我们不建议直接使用~/.acme.sh/目录下的证书文件因为续期时这些文件会被更新。更好的做法是让acme.sh在每次证书更新后自动将新证书复制到标准位置如/etc/ssl/并重载Nginx。首先为你的域名创建一个存放证书的目录通常遵循系统惯例sudo mkdir -p /etc/ssl/example.com/然后执行安装命令。这是一个需要仔细配置的一步~/.acme.sh/acme.sh --install-cert -d *.example.com \ --key-file /etc/ssl/example.com/private.key \ --fullchain-file /etc/ssl/example.com/fullchain.cer \ --reloadcmd sudo systemctl reload nginx命令参数详解--install-cert告诉acme.sh要安装证书。-d *.example.com指定为哪个域名安装证书。--key-file指定私钥文件拷贝到的目标路径。--fullchain-file指定完整证书链文件拷贝到的目标路径。--reloadcmd这是核心指定一个在证书安装/更新后需要执行的命令。这里我们设置为重载Nginx配置sudo systemctl reload nginx。reload是平滑重载不会中断现有连接。执行这个命令后acme.sh会立即将当前的证书和密钥拷贝到指定位置。更重要的是它会记住这个“安装配置”。以后每次该证书自动续期成功后acme.sh都会自动执行这三步1. 拷贝新证书和密钥到指定位置2. 执行--reloadcmd定义的命令。这样就实现了真正的全自动续期。5.2 配置Nginx以使用新证书现在证书已经就位我们需要修改Nginx的站点配置文件来使用它们。假设你有一个Nginx配置文件/etc/nginx/sites-available/your-site。找到配置中监听443端口HTTPS的server块修改ssl_certificate和ssl_certificate_key指令指向我们刚才安装的证书路径server { listen 443 ssl http2; server_name app.example.com www.example.com; # 指向acme.sh自动安装的证书路径 ssl_certificate /etc/ssl/example.com/fullchain.cer; ssl_certificate_key /etc/ssl/example.com/private.key; # 其他SSL优化配置可选但推荐 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # ... 其他配置如root, index, location等 }修改完成后检查Nginx配置语法是否正确sudo nginx -t如果显示“syntax is ok”和“test is successful”就可以安全地重载Nginx了sudo systemctl reload nginx现在用浏览器访问你的HTTPS站点应该能看到绿色的安全锁证书信息显示是由“Let‘s Encrypt”签发并且适用于你的泛域名。实操心得在--reloadcmd中使用sudo需要配置免密码。可以通过visudo命令编辑sudoers文件为运行acme.sh的用户通常是当前用户添加一条规则允许其无需密码执行systemctl reload nginx。例如yourusername ALL(ALL) NOPASSWD: /bin/systemctl reload nginx。这是实现自动化至关重要的一步否则续期后无法自动重载Nginx。6. 自动续期机制与状态监控“一次配置永久有效”的秘密就在于acme.sh内置的自动续期机制。理解并验证这个机制你才能高枕无忧。6.1 续期原理与计划任务acme.sh在安装时就自动为你创建了一个cronjob定时任务。你可以通过以下命令查看crontab -l你应该能看到一行类似这样的记录具体路径可能略有不同0 0 * * * /home/username/.acme.sh/acme.sh --cron --home /home/username/.acme.sh /dev/null这行配置的意思是每天凌晨0点0分执行一次acme.sh --cron命令。--cron参数会让acme.sh检查所有由它管理的证书如果发现有证书距离过期时间小于30天这是Let‘s Encrypt允许续期的最早时间它就会自动触发续期流程。续期流程和首次申请几乎一样使用DNS-01验证获取新证书然后根据你之前用--install-cert设置的参数将新证书安装到指定位置并执行重载命令。整个过程在后台静默完成。6.2 手动测试与状态检查在依赖自动续期之前强烈建议手动测试一下续期流程是否畅通。你可以使用--force参数来强制更新一张证书即使它还没到期。~/.acme.sh/acme.sh --renew -d *.example.com --force观察命令输出看它是否成功完成了“验证 - 获取新证书 - 安装证书 - 执行reloadcmd”的全流程。同时检查/etc/ssl/example.com/目录下证书文件的修改时间是否更新了并用浏览器访问网站确认新证书已生效。为了监控证书状态你可以定期运行以下命令查看所有由acme.sh管理的证书的过期时间~/.acme.sh/acme.sh --list这个命令会以表格形式列出域名、证书存放路径、密钥路径以及最重要的到期日。你可以把它加入到自己的监控脚本中或者设置一个每月检查一次的日历提醒双重保险。6.3 续期失败排查与日志分析自动续期虽好但并非万无一失。网络问题、API密钥失效、DNS服务商故障、磁盘空间不足等都可能导致续期失败。acme.sh会将每次cronjob的执行日志输出到标准错误但我们在crontab里将其重定向到了/dev/null。为了调试你可以修改crontab将日志输出到一个文件0 0 * * * /home/username/.acme.sh/acme.sh --cron --home /home/username/.acme.sh /home/username/acme_cron.log 21这样每天的续期尝试日志都会追加到acme_cron.log文件中。如果续期失败你可以查看这个日志文件来定位问题。常见的失败原因及解决思路阿里云API密钥失效RAM用户的AccessKey可能被禁用或删除。去阿里云控制台检查并更新密钥然后更新~/.acme.sh/account.conf文件或环境变量。DNS解析超时在验证阶段脚本查询TXT记录超时。可以尝试在签发命令中加入--dnssleep参数增加等待DNS传播的时间例如--dnssleep 120等待120秒。证书安装失败--reloadcmd命令执行出错。检查命令路径是否正确sudo权限是否已配置。可以手动执行一遍sudo systemctl reload nginx测试。Let‘s Encrypt速率限制对于同一个域名或一组域名Let‘s Encrypt有每周的签发次数限制。失败日志中通常会明确提示。遇到这种情况只能等待限制解除。切勿在短时间内频繁重试这只会加剧限制。个人经验我习惯在每次服务器重大变更如系统升级、迁移后手动运行一次--renew --force来测试整个证书链路是否正常。同时建议在服务器监控如Prometheus Grafana或简单的健康检查脚本中加入对证书过期时间的检查比如提前15天告警这样即使自动续期失败你也有充足的人工干预时间。

相关新闻

AI编程助手核心差异:从模型能力到工程化框架的深度解析

AI编程助手核心差异:从模型能力到工程化框架的深度解析

1. 从“Agent Model Harness”的公式聊起最近在折腾各种AI编程工具时,脑子里总绕不开一个简单的公式:Agent Model Harness。这个公式乍一看像句废话,但仔细琢磨,它恰恰是理解当前市面上各种AI编程助手(比如Cursor、…

2026/10/15 1:57:06 阅读更多 →
《上古卷轴5》MOD材质汉化与批量管理:从xTranslator到批处理脚本

《上古卷轴5》MOD材质汉化与批量管理:从xTranslator到批处理脚本

在《上古卷轴5:天际》的MOD制作与整合过程中,材质模型的替换与汉化是提升游戏沉浸感和本地化体验的核心环节。一个高质量的材质MOD,例如“黑暗兄弟会黑曜石大门”,不仅能显著改善特定场景的视觉表现,其背后涉及的资源管…

2026/10/14 6:18:53 阅读更多 →
网络设备全解析:从物理层到应用层,构建高效网络架构

网络设备全解析:从物理层到应用层,构建高效网络架构

最近在整理网络设备知识时,发现很多资料要么过于理论化,要么零散不成体系,导致很多朋友在学习和工作中难以快速抓住重点。本文将围绕核心网络设备,用最精炼的语言和清晰的逻辑,在短时间内为你构建一个完整的知识框架。…

2026/10/4 21:11:03 阅读更多 →

最新新闻

ECharts柱状图配置全解析:从基础到动态排序与性能优化

ECharts柱状图配置全解析:从基础到动态排序与性能优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 1:57:11 阅读更多 →
LVGL 图像按钮(lv_imgbtn)实战指南:Simple Image button 示例全解与源码原理

LVGL 图像按钮(lv_imgbtn)实战指南:Simple Image button 示例全解与源码原理

嵌入式物联网智能硬件嵌入式GUI硬件开发 【免费下载链接】HoloCubic 带网络功能的伪全息透明显示桌面站 项目地址: https://gitcode.com/gh_mirrors/ho/HoloCubic 点击查看 免费下载 本篇文章围绕 HoloCubic 固件所采用的 LVGL 图形库中的 lv_imgbtn(图…

2026/10/15 1:57:11 阅读更多 →
深入解析 LAMMPS `compute msd/chunk`:逐块均方位移的计算原理与实战配置

深入解析 LAMMPS `compute msd/chunk`:逐块均方位移的计算原理与实战配置

科研科学计算高性能计算 【免费下载链接】lammps Public development project of the LAMMPS MD software package 项目地址: https://gitcode.com/gh_mirrors/la/lammps 点击查看 免费下载 导读 compute msd/chunk 是 LAMMPS 中用于按 chunk(原子分组…

2026/10/15 1:57:11 阅读更多 →
车机双系统下用ASAN抓内存踩踏:8155p上Android.mk与Android.bp配置实战

车机双系统下用ASAN抓内存踩踏:8155p上Android.mk与Android.bp配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 1:57:11 阅读更多 →
Go 1.27.1 内存局部性调优实录:压榨 CPU 缓存行命中率的工程实践

Go 1.27.1 内存局部性调优实录:压榨 CPU 缓存行命中率的工程实践

在微服务吞吐量达到数万 QPS 的高性能网关或高频事件调度引擎中,单纯依赖算法时间复杂度(如将 $O(N)$ 降为 $O(1)$)已经无法进一步带来数量级的性能突破。很多底层服务开发者发现,即便 CPU 利用率跑满了 100%,服务处理…

2026/10/15 1:57:11 阅读更多 →
Start Bootstrap 主题 One Page Wonder 详解:基于 Bootstrap 4 快速构建单页网站

Start Bootstrap 主题 One Page Wonder 详解:基于 Bootstrap 4 快速构建单页网站

前端文档 【免费下载链接】startbootstrap A library of free and open source Bootstrap themes and templates 项目地址: https://gitcode.com/gh_mirrors/st/startbootstrap 点击查看 免费下载 One Page Wonder 是 Start Bootstrap 开源仓库中的一款经典单页主题…

2026/10/15 1:56:09 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →