个人博客自建指南(7.2 番外):草稿与私密文章 —— 构建但不公开
前言
第7篇我们实现了 git push 直达线上,从此写文章、部署一气呵成。7.1番外我们配置了基础的SEO,但随之而来一个问题:有些文章写了一半、有些是测试笔记、有些暂时不想公开——它们也在 docs/ 目录下,也会被构建、被放进 sitemap.xml、被搜索引擎发现。
当前需要的是:构建存在,自己能看,但搜索引擎和访客都找不到入口。
这篇就来解决这个问题。做完之后,你的站点将拥有一个“只有你知道”的私人区域。
一、方案选择
| 方案 | 优点 | 缺点 | 推荐度 |
|---|---|---|---|
直接不放 docs/ 里 | 最干净 | 本地预览不方便,还得切目录 | ❌ |
| 只不加导航/侧边栏 | 简单 | sitemap 仍会暴露,搜索引擎仍可能收录 | ❌ |
| noindex + sitemap 过滤 + robots 禁止 | 三层保险,自己可访问 | 需一次性配置 | ✅ 推荐 |
搜索引擎优化是让该被看到的被看到,而这篇是做相反的事:让不该被看到的,不被看到。
1.1 架构总览
本地写 md(放在 docs/other/ 下)
→ pnpm build 构建时:
① sitemap 用 transformItems 过滤 /other/ 目录
② 自动给 /other/ 页面注入 noindex 标签
③ robots.txt 禁止抓取 /other/
→ 部署到 ECS
→ 搜索引擎:看不到,不收录
→ 访客:没有链接可以点进来
→ 你自己:直接输入 URL 即可访问全程不涉及密码验证、不涉及 .htaccess、不涉及后端鉴权。纯粹靠搜索引擎协议和站点配置来实现“隐藏”。
1.2 前置条件
- 已完成第7、7.1篇的全部步骤(自动化部署已就绪,SEO基础配置已完成)
- 你的草稿/私密文章位于
docs/other/目录下(或其他你指定的目录) - 这些文章已有基本的 frontmatter
二、sitemap 过滤私密目录
搜索引擎发现新页面的主要途径之一就是 sitemap。如果私密文章出现在 sitemap.xml 里,就等于主动把入口告诉了搜索引擎。
在 .vitepress/config.ts 中找到 sitemap 配置,加上 transformItems 钩子:
sitemap: {
hostname: 'https://jiejinx.cn',
transformItems: (items) => {
// 过滤掉 /other/ 下的私密页面
return items.filter(i => !i.url.includes('/other/'))
},
},transformItems 是 VitePress 官方提供的钩子,在生成 sitemap.xml 之前执行。它接收所有待输出的条目,返回的数组就是最终写入文件的条目。这里用 filter 把 URL 中包含 /other/ 的条目剔除。
执行 pnpm build 后,检查生成的 sitemap.xml:
grep "other" docs/.vitepress/dist/sitemap.xml如果没有输出,说明过滤成功。
三、自动注入 noindex 标签
sitemap 过滤了,但如果搜索引擎通过其他途径(比如外部链接、书签导入、直接猜测 URL)访问到这些页面,它仍然可能被收录。需要在页面级别告诉搜索引擎:不要索引我。
VitePress 提供了一个钩子 transformHead,可以在构建时根据页面路径动态修改 <head> 标签。在 .vitepress/config.ts 中添加:
export default defineConfig({
// ... 其他配置
transformHead({ pageData }) {
// 如果页面路径以 other/ 开头,注入 noindex
if (pageData.relativePath.startsWith("other/")) {
return [["meta", { name: "robots", content: "noindex, nofollow" }]];
}
},
});pageData.relativePath 是相对于 docs/ 目录的文件路径,比如 other/other1.md。只要它以 other/ 开头,就自动注入 <meta name="robots" content="noindex, nofollow">。
部署后,打开 https://jiejinx.cn/other/other1,查看页面源代码,确认 <head> 中包含:
<meta name="robots" content="noindex, nofollow" />若页面无法正常加载出来,可能是nginx配置中的try_files没有设置html后缀导致,ECS上执行以下命令:
# 更新配置
sudo sed -i 's|try_files $uri $uri/ /index.html;|try_files $uri $uri.html $uri/ /index.html;|' /etc/nginx/conf.d/jiejinx.cn.conf
#重启nginx
sudo nginx -t && sudo systemctl reload nginx刷新线上页面查看页面head内容。
四、robots.txt 禁止抓取
即使 sitemap 过滤了、页面加了 noindex ,爬虫仍然可能通过其他链接发现这些页面并尝试抓取。robots.txt 是最后一道防线:直接告诉爬虫,这个目录不要进来。
编辑 docs/public/robots.txt:
User-agent: *
Allow: /
Disallow: /other/
Sitemap: https://jiejinx.cn/sitemap.xml部署后访问 https://jiejinx.cn/robots.txt,确认内容正确。
五、不在导航和侧边栏出现
这是最基础的一步。检查你的 nav 和 sidebar 配置,确保没有指向 /other/ 的链接。
如果你之前为了自己方便加过,现在删掉。如果你需要快速访问私密文章,建议用浏览器书签,而不是在站点上留入口。
六、不让草稿被搜索到
第五步解决了导航和侧边栏的入口问题,但还有一个隐蔽的漏洞:VitePress 的本地搜索。
如果你启用了 provider: 'local' 的搜索功能,它默认会把所有被构建进 dist/ 的页面都编入索引——包括你放在 docs/other/ 下的草稿。这意味着,即使导航栏和侧边栏没有入口,读者仍然可以通过搜索框搜到草稿内容。
有两种处理方式,按你的需求选一种即可。
1. 按文件单独屏蔽(轻量,适合偶尔写草稿)
在每个草稿文件的 frontmatter 中加一行:
---
search: false
---这样 VitePress 本地搜索会跳过该页,不建立索引。
优点:按文件控制,粒度细。
缺点:每篇草稿都得记得加,忘了就漏网。
2. 按路径批量屏蔽(治本,推荐)
在 docs/.vitepress/config.ts 的 search.options 中加 _render 函数,按路由路径过滤:
search: {
provider: 'local',
options: {
// 搜索页面渲染过滤指定路径下的页面
_render(src, env, md) {
// 如果路径以 /other/ 开头,返回空字符串跳过索引
if (env.relativePath.startsWith('other/')) {
return ''
}
// 正常渲染
return md.render(src, env)
},
//locales...中文配置
}
}path 是相对于 docs/ 的路由路径,以 / 开头。
优点:配置一次,整个目录下的草稿永久不进搜索。
优点:新增草稿无需额外操作,自动屏蔽。
3. 构建期就让草稿不参与(最干净)
如果你希望草稿文件连 dist/ 都不进(线上完全不存在),可以在 config.ts 顶层加 srcExclude:
export default defineConfig({
// 草稿不参与构建
srcExclude: ['other/**/*.md'],
themeConfig: {
search: { provider: 'local' }
}
})这样 pnpm run build 时草稿文件直接被忽略,dist 里没有对应 HTML,本地搜索自然也搜不到。
缺点:本地 dev 预览草稿时需要临时注释掉这行配置,略麻烦。
七、完整配置一览
把以上所有配置汇总到 .vitepress/config.ts 中:
// docs/.vitepress/config.ts
import { defineConfig } from "vitepress";
export default defineConfig({
title: "阶进遐的技术随笔",
description: "阶进遐(jiejinx.cn)的个人技术博客",
lang: "zh-CN",
// 站点地图
sitemap: {
hostname: "https://jiejinx.cn",
transformItems: (items) => {
// 过滤掉 other/ 下的私密页面
return items.filter((i) => !i.url.includes("other/"));
},
},
// 动态注入 noindex
transformHead({ pageData }) {
if (pageData.relativePath.startsWith("other/")) {
return [["meta", { name: "robots", content: "noindex, nofollow" }]];
}
},
// 主题配置
themeConfig: {
// ... 你的 nav、sidebar 等配置
search: {
provider: 'local',
options: {
// 草稿目录的路由前缀过滤
_render(src, env, md) {
// 如果路径以 /other/ 开头,返回空字符串跳过索引
if (env.relativePath.startsWith('other/')) {
return ''
}
// 正常渲染
return md.render(src, env)
},
//locales...中文配置
}
}
},
});robots.txt文件配置(docs/public/robots.txt):
User-agent: *
Allow: /
Disallow: /other/
Sitemap: https://jiejinx.cn/sitemap.xml八、验证清单
部署后逐一检查:
| 检查项 | 方法 | 预期结果 |
|---|---|---|
| sitemap 已过滤 | https://jiejinx.cn/sitemap.xml | 没有 /other/ 开头的 URL |
| noindex 已注入 | 查看 /other/other1 页面源码 | <meta name="robots" content="noindex, nofollow"> |
| robots.txt 正确 | https://jiejinx.cn/robots.txt | 包含 Disallow: /other/ |
| 站内搜索 | 用草稿文章内的关键词搜索 | 没有草稿文章的搜索结果出现 |
| 无公开链接 | 浏览全站,检查所有页面 | 没有指向 /other/ 的链接 |
| 自己可访问 | 直接输入 URL | 页面正常显示 |
九、日常维护
新增私密文章
直接把 .md 文件放到 docs/other/ 目录下,不需要修改任何配置。transformItems 和 transformHead 是基于路径模式匹配的,会自动覆盖新文件。
将私密文章转为公开
把文件从 docs/other/ 移动到公开目录(如 docs/self-host/),然后在 nav 或 sidebar 中添加链接。sitemap 会自动包含它,noindex 也不会再注入。
查看所有私密文章列表
如果你想知道自己有哪些私密文章,可以直接在本地文件管理器中查看 docs/other/ 目录,或者在 VSCode 中浏览。不需要在站点上建索引页。
十、故障排查
1. sitemap 中仍有 /other/ 页面
检查 transformItems 中的过滤条件。i.url 是完整的 URL 字符串(如 https://jiejinx.cn/other/other1),确认你的条件 includes('/other/') 能匹配到。
2. noindex 没有生效
检查 transformHead 中的路径判断条件。pageData.relativePath 的值是相对于 docs/ 的路径,不包含前导斜杠。确认你的文件路径确实以 other/ 开头。
3. 自己忘了 URL 怎么办
私密文章的 URL 由 文件名 决定。如果你docs/other/ 目录下有 16 篇 other 文章,文件名依次为,
`other1.md`
`other1.md`
...
`other16.md`那么,文件对应的URL 分别是:
https://jiejinx.cn/other/other1
https://jiejinx.cn/other/other2
...
https://jiejinx.cn/other/other16建议在本地维护一个私密 URL 列表,或者直接记住命名规则。
十一、总结
这篇我们实现了:
| 需求 | 做法 |
|---|---|
| 搜索引擎不收录 | noindex + sitemap 过滤 + robots 禁止抓取 |
| 访客找不到入口 | 不在导航/侧边栏出现,不在任何页面生成链接,站内无法搜索出结果 |
| 自己能访问 | 直接输入 URL,页面正常渲染 |
| 新增私密文章 | 直接放文件,无需改配置 |
| 转为公开 | 移动文件 + 加导航链接 |
从此你的站点拥有了两层空间:
- 公开层:导航可见、sitemap 包含、搜索引擎可收录
- 私密层:构建存在、自己可看、搜索引擎和访客都找不到
结合第7篇的自动化部署,你的博客工作流现在是这样的:
公开文章:写 md → git push → 自动部署 → 搜索引擎收录
私密文章:写 md → git push → 自动部署 → 只有自己知道两层互不干扰,一条流水线打通。