Halo 根目录文件配置指南:ads.txt 与各类网站验证

17 阅读

做 AdSense、申请微信公众号 JS 安全域名、接入百度搜索资源平台或 Google Search Console 时,平台经常会要求你下载一个验证文件,放到网站根目录,并确保 https://你的域名/文件名 可以直接访问。

如果你习惯了静态网站,这件事就是把文件丢进网站根目录。但 Halo 是动态站点,没有传统意义上的「网站根目录」,所以不少用户会卡在这里:文件传到了主题目录、附件目录,或者 Halo 容器里,结果平台访问到的仍然是 404。

Halo 其实内置了根目录文件的入口:工作目录下的 static 目录。除此之外,也可以让 Nginx 直接返回文件。下面分别说明。

本文以 Halo 2.x 为例。

先确认:文件必须出现在域名根路径

不同平台要求的文件名不一样,但共同点是:文件必须能通过「域名 + 文件名」直接访问,不能多一层路径,也不能返回登录页或主题的 404 页面。

平台常见文件名要求的访问地址
ads.txt(广告系统)ads.txthttps://example.com/ads.txt
微信公众号MP_verify_xxxxxxxx.txthttps://example.com/MP_verify_xxxxxxxx.txt
百度搜索资源平台baidu_verify_codeva-xxxxxxxx.htmlhttps://example.com/baidu_verify_codeva-xxxxxxxx.html
Google Search Consolegoogle1234abcd.htmlhttps://example.com/google1234abcd.html

上表中的文件名只是示例,实际以平台下载给你的文件名为准,不要自行改名。

如果站点同时使用 example.com 和 www.example.com,请按平台提示的地址验证;不确定时,确认两个域名都能访问到同一份文件,避免平台检查的地址和你以为的不一致。ads.txt 按规范应放在根域名下,微信公众号如果配置的是带路径的业务域名,验证文件则要放到对应子目录。

这些文件不一定非得由 Halo 生成。只要访问对应 URL 时能返回正确内容即可,所以既可以交给 Halo,也可以交给 Nginx。

为什么不能放进主题或附件目录

Halo 主题的静态资源统一放在主题的 templates/assets/ 下,最终访问路径是 /themes/主题名/assets/...;文章附件默认在 /upload/... 下。两者都不是网站根目录。

把 ads.txt 放进主题的静态资源目录,它的地址会变成 https://example.com/themes/你的主题/assets/ads.txt。验证平台访问 https://example.com/ads.txt 时,依然找不到文件。

方案一:使用工作目录下的 static 目录(推荐)

Halo 官方文档把 static 描述为「虚拟的根文件目录」。准确地说,Halo 会把 <工作目录>/static 注册为站点根路径下的静态资源目录,于是:

<工作目录>/static/ads.txt
→ https://example.com/ads.txt

<工作目录>/static/.well-known/foo.txt
→ https://example.com/.well-known/foo.txt

需要注意的是,static 目录默认不存在,需要手动创建。它不是 Docker 卷,也不是主题目录,而是 Halo 工作目录的一部分。只要工作目录已经映射到宿主机,static 就会跟着出现在映射目录里,不需要额外加一条 -v。

第一步:找到 Halo 的工作目录

不同部署方式的工作目录不一样。最稳妥的办法是直接看容器把宿主机的哪个目录映射到了 /root/.halo2:

docker ps --format '{{.Names}}\t{{.Image}}' | grep halo
docker inspect <容器名> --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'

在输出中找到 Destination 为 /root/.halo2 的那一行,箭头左边的 Source 就是宿主机上的工作目录。进入这个目录,在里面创建 static 即可。如果你使用 Podman,把上面的 docker 换成 podman。

常见部署方式的对应关系如下:

部署方式宿主机上的工作目录
Docker / Podman 单容器-v 左侧的目录,官方示例是 ~/.halo2
Docker Composecompose 文件中 :/root/.halo2 左侧的目录,官方示例是 ./halo2
1Panel 应用商店/opt/1panel/apps/halo/<应用名>/data,默认安装根目录为 /opt;如果应用名是 halo,就是 /opt/1panel/apps/halo/halo/data
宝塔 Docker 应用以应用模板的挂载目录为准,建议用上面的 docker inspect 确认
JAR + systemdhalo.work-dir 指向的目录,默认是 ~/.halo2
Helm / Kubernetes容器内 HALO_WORK_DIR=/data/halo,对应 PVC 的挂载点

如果你在启动参数或环境变量里改过 halo.work-dir、HALO_WORK_DIR,请以实际值为准。

Helm 部署可以通过 kubectl exec 进入 Pod,在 /data/halo 下创建 static,或者用 kubectl cp 把文件复制进去。

第二步:创建目录并放入文件

以 Docker 挂载到 ~/.halo2 为例:

mkdir -p ~/.halo2/static
cp /path/to/ads.txt ~/.halo2/static/ads.txt
chmod 644 ~/.halo2/static/ads.txt

如果使用 1Panel 或宝塔,可以直接在面板的「文件」中进入对应目录,新建 static 文件夹,再上传文件,不需要命令行。目录权限保持 755,文件权限保持 644,确保运行 Halo 的进程能读取即可。

第三步:验证

curl -sS -o /dev/null -w '%{http_code} %{content_type}\n' https://example.com/ads.txt
curl https://example.com/ads.txt

期望结果是 200、text/plain(ads.txt 要求 Content-Type 为 text/plain),并且返回内容与文件一致。如果看到 301 或 302,可以用 curl -IL https://example.com/ads.txt 查看跳转链,确认最终地址和内容。

验证平台通常要求直接返回 200,不要返回登录页、主题的 404 页面,也不要跳转到其他域名。

注意事项

  • static 目录必须手动创建,Halo 不会自动生成,控制台里也没有管理入口,需要通过 SSH 或服务器面板的文件管理来维护。
  • Linux 服务器上文件名区分大小写,ads.txt 和 Ads.txt 是两个文件。
  • 文件名和内容以平台给出的为准,不要改名,也不要让编辑器额外添加 BOM 或 HTML 标签。
  • 验证通过后不要删除文件。百度、Google 等平台会定期复查,微信公众号也可能再次校验。
  • 文件是按请求从磁盘读取的,放进去后通常不需要重启 Halo;如果浏览器或 CDN 有缓存,用无痕窗口或刷新缓存后再试。
  • 如果使用 Docker,建议直接把文件放在宿主机映射目录中。没有映射到宿主机的容器内路径,在容器重建后会丢失;docker cp 是否持久化,取决于目标路径有没有被映射出来。
  • Halo 自带的备份会打包工作目录,static 中的文件也会一起备份。如果你使用 Nginx 方案,文件不在工作目录内,需要单独备份。
  • 如果某个路径已经被插件占用(例如 robots.txt、sitemap.xml),先确认实际返回的是谁的内容,避免冲突。

方案二:用 Nginx 直接返回文件

如果你不想动 Halo 的工作目录,或者 Halo 跑在 Kubernetes、容器里不方便拷贝文件,可以让 Nginx 直接返回这些文件。请求在 Nginx 层就被处理,不会进入 Halo。

准备文件

sudo mkdir -p /var/www/halo-static
sudo cp ads.txt /var/www/halo-static/
sudo chmod 644 /var/www/halo-static/ads.txt

目录只要 Nginx 进程能读即可。Debian/Ubuntu 上 Nginx 用户通常是 www-data,CentOS/RHEL 上通常是 nginx。

修改 server 配置

在负责这个域名的 server 块中,添加一个精确匹配的 location:

server {
    listen 443 ssl;
    server_name example.com;

    # 精确匹配,只拦截这一个文件
    location = /ads.txt {
        root /var/www/halo-static;
        default_type text/plain;
        charset utf-8;
    }

    location / {
        proxy_pass http://127.0.0.1:8090;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

root /var/www/halo-static; 配合 location = /ads.txt,实际读取的是 /var/www/halo-static/ads.txt。也可以写成 alias /var/www/halo-static/ads.txt;,效果相同。

如果内容很短,也可以完全不创建文件,直接用 return 返回:

# ads.txt:单行内容
location = /ads.txt {
    default_type text/plain;
    return 200 "google.com, pub-1234567890, DIRECT, f08c47fec0942fa0";
}

# 微信公众号验证文件:单行 token
location = /MP_verify_xxxxxxxx.txt {
    default_type text/plain;
    return 200 "xxxxxxxx";
}

Nginx 的 default_type 默认就是 text/plain,但如果你在别处改过,建议在 location 里显式写上。return 不会自动补换行,内容里的引号和 $ 也要按 Nginx 的规则转义;多行内容或 HTML 验证文件建议还是用静态文件,避免换行和转义出错。

如果有多个文件,就写多个精确匹配:

location = /MP_verify_xxxxxxxx.txt { root /var/www/halo-static; }
location = /baidu_verify_codeva-xxxxxxxx.html { root /var/www/halo-static; }
location = /google1234abcd.html { root /var/www/halo-static; }

文件很多时,可以用正则一次性匹配,但要把范围收窄,避免误伤正常页面:

location ~ ^/(ads\.txt|MP_verify_[A-Za-z0-9]+\.txt|baidu_verify_[A-Za-z0-9-]+\.html|google[0-9a-z]+\.html)$ {
    root /var/www/halo-static;
}

正则中的文件名规则只是示例,实际以平台给的文件名为准。

测试并重载

sudo nginx -t
sudo nginx -s reload

然后按前面的 curl 命令验证。精确匹配的 location = 优先级高于 location /,所以这些请求不会进入 Halo。

注意事项

  • 配置必须写在正确的 server 块里,通常是监听 443、server_name 为你的域名的那个块,而不是默认站点。
  • 如果 Nginx 也在 Docker 里,nginx -t 要在容器内执行,文件也要放在容器能读到的挂载目录中,不能只放在宿主机任意路径。
  • 也可以不存文件,直接用 return 200 "内容"; 返回。单行 token(例如微信公众号验证文件、单行 ads.txt)很适合这种方式;但要注意 Content-Type 为 text/plain、内容与平台给的一致,并且内容里的引号和 $ 要正确转义。多行 ads.txt 或 HTML 验证文件建议仍用静态文件。
  • Nginx 返回的文件不在 Halo 工作目录内,Halo 自带备份不会包含它们,需要单独备份。
  • 如果前面还有 CDN 或 WAF,记得刷新缓存,并确认没有拦截验证平台的爬虫。
  • 1Panel 的网站使用 OpenResty,可以在网站设置的反向代理或配置文件中添加;宝塔则在网站的配置文件中添加。

两种方案怎么选

对比项static 目录Nginx
文件位置<工作目录>/staticNginx 可读的目录,或直接用 return 返回内容
是否经过 Halo是否
随 Halo 备份是否,需要单独备份;用 return 时没有文件需要备份
适合场景大多数场景,尤其是 Docker、1Panel无法进入工作目录、文件较多、希望由 Web 服务器直接返回
维护成本放文件即可,通常无需重启需要修改配置并 reload

一般来说优先用 static:不需要改 Nginx,升级 Halo 不会影响,迁移时也会跟着工作目录一起走。只有在你无法访问工作目录,或者文件很多、想让 Nginx 统一处理时,才选择反向代理方案。

常见问题

访问返回 404

先确认文件确实在 <工作目录>/static 下,而不是容器的临时目录;再确认文件名大小写和平台给的一致;最后确认请求经过的是你以为的那台服务器。用 docker inspect 查到的挂载目录,通常比凭印象猜测更可靠。

访问返回 403

检查目录和文件权限,确保运行 Halo 的用户能读。Docker 官方镜像通常以 root 运行,JAR 方式则要注意 systemd 中 User= 指定的用户。

返回的是主题的 404 页面

这说明请求没有命中静态文件,或者 Nginx 的 location 没有生效。检查配置是否写在正确的 server 块中,可以用 nginx -T | grep ads.txt 确认最终生效的配置。

平台提示验证失败

检查是否有 301/302 跳转、是否返回了登录页或 404、是否被 CDN 缓存。微信和百度都要求文件能直接访问且内容一致;ads.txt 还要求 Content-Type: text/plain。

更换文件后仍然是旧内容

Halo 的静态资源默认带较长缓存(默认 max-age=365d),CDN 也可能缓存。刷新 CDN 缓存,或者临时换一个文件名再验证。

Docker 里放了文件但没生效

先确认容器的 /root/.halo2 到底映射到了宿主机哪个目录。文件最好直接放在这个映射目录里;如果写进了没有映射出来的容器内路径,容器重建后修改就会丢失。

参考


评论