前言
本篇聚焦 DoraTiger 主题的安全功能和 SEO 优化。安全方面涵盖文章加密(AES-256-GCM)和外链重定向拦截;SEO 方面涵盖原生 Sitemap/Robots.txt 生成器和 JSON-LD 结构化数据。这些功能最早都是通过第三方插件实现的,在主题重构时选择集成到内部,主要是为了更好的适配性。
一、文章加密
1.1 为什么需要加密
加密功能最初也是用的第三方插件(hexo-blog-encrypt),重构时一体化集成到主题内部。原因是加密功能和文章渲染管线耦合很深 — 加密后的文章需要特殊的渲染流程(先解密再渲染),第三方插件的实现和主题的渲染管线配合起来有各种边界问题。
目前只加密了一篇文章 — 本科同学录。属于私密内容,不适合公开。
1.2 加密方案
采用构建时加密 + 运行时解密的方案,无需后端服务:
text123456构建时(Node.js 服务端): 明文内容 → PBKDF2 密钥派生(100K 迭代)→ AES-256-GCM 加密 → 加密数据 + Salt + IV + Auth Tag 嵌入 HTML data 属性 运行时(浏览器 Web Crypto API): 用户输入密码 → PBKDF2 派生相同密钥 → AES-GCM 解密 → 显示内容
1.3 构建端加密
javascript12345678910// scripts/filters/lib/encrypt.js function encryptContent(content, password) { const salt = crypto.randomBytes(16); // 16 字节随机盐值 const iv = crypto.randomBytes(12); // 12 字节随机 IV const key = crypto.pbkdf2Sync( // PBKDF2 派生密钥 password, salt, 100000, 32, "sha256" // 100K 迭代,SHA-256 ); const cipher = crypto.createCipheriv("aes-256-gcm", key, iv); // ... 加密 + 提取 auth tag }
1.4 运行时解密
javascript1234567891011// 内联在 footer.pug 中 async function decrypt(pwd) { var keyMat = await crypto.subtle.importKey('raw', ...); var key = await crypto.subtle.deriveKey({ name: 'PBKDF2', salt, iterations: 100000, hash: 'SHA-256' }, keyMat, ...); var pt = await crypto.subtle.decrypt({ name: 'AES-GCM', iv, tagLength: 128 }, key, combined); return new TextDecoder().decode(pt); }
1.5 使用方式
yaml1234# front-matter password: "my-secret-password" abstract: "本文已加密,请输入密码查看" # 可选,覆盖默认提示 message: "请输入密码" # 可选,覆盖默认按钮文字
加密后对搜索引擎的影响:加密页面的内容对爬虫不可见,不会被收录。对于私密内容这正是期望的效果。
二、外链重定向拦截
2.1 从公安备案到外链安全
这个功能的由来有一个小故事。博客最初只做了 ICP 备案,后来接到公安电话通知需要做公安备案。备案完成后,自然就考虑到了外链安全性的问题 — 如果博客上的链接指向了恶意网站,作为站长是有责任的。
最早是利用 Yourls 短链服务做的外链跳转 — 每个外链先转成短链,用户点击时经过 Yourls 中间页。有一段时间 Hexo 的提交记录全是改链接的,把旧的直接链接替换成短链格式(顺便说一下,这也催生了那篇 服务器操作指北(6)Yourls 短链接服务部署)。
但后来觉得每次加外链都得去 Yourls 后台手动加一条记录,太麻烦了。就像图片之前用七牛云 CDN,最后也是嫌麻烦放弃了。所以参考知乎的外链跳转风格,构建了一个主题自用的方案 — 构建时自动识别外链,运行时通过中间页拦截。同时因为做了 网站公安备案小记,外链安全性也成了必须考虑的问题。
2.2 构建时注入
javascript12345678910// scripts/filters/lib/redirect.js data.content = data.content.replace( /<a\s+([^>]*?)href=["']([^"']+)["']([^>]*)>/gi, (match, pre, url, post) => { if (!shouldRedirect(url)) return match; return `<a ... class="external-link" data-redirect="${encodeURIComponent(url)}" target="_blank" ...>`; } );
2.3 运行时拦截
pug123456789// layout/_include/_layout.pug script. document.addEventListener('click', (e) => { const link = e.target.closest('a[data-redirect]'); if (link) { e.preventDefault(); window.open('/redirect/?url=' + link.dataset.redirect); } });
2.4 配置
yaml12345redirect: method: exclude # exclude(默认)| include exclude: - "superheaoz.top" # 自己的域名不拦截 - "github.com"
三、原生 Sitemap 与 SEO
3.1 从插件到内置
Sitemap 和 Robots.txt 最初也是用的第三方插件(hexo-generator-sitemap),重构时一体化集成。原因是这两个生成器逻辑不复杂,但和主题的配置体系(theme-config())结合后可以做到更精细的控制。
3.2 Sitemap 生成器
支持 XML、TXT 或两者同时输出,优先级和更新频率可通过配置调整:
javascript123456789101112131415161718// scripts/generators/lib/sitemap.js module.exports = function (locals) { const urls = []; urls.push({ loc: siteUrl + "/", freq: "daily", priority: 1.0 }); locals.posts.sort("-date").forEach(post => { urls.push({ loc: siteUrl + "/" + post.path, freq: changefreq, priority: 0.8, lastmod: post.updated }); }); if (format === "xml" || format === "both") results.push({ path: "sitemap.xml", data: buildXml(urls) }); if (format === "txt" || format === "both") results.push({ path: "sitemap.txt", data: buildTxt(urls) }); };
3.3 SEO 实际效果
这个博客主要还是以学习为目的,没有专门做 SEO 优化。简单对接了百度、谷歌、微软的站长统计功能 — 提交了 sitemap,配置了 robots.txt,加了 JSON-LD 结构化数据。对搜索引擎收录有一定帮助,但没有做过系统的 SEO 策略分析。
3.4 JSON-LD 结构化数据
pug1234567891011121314151617// layout/_include/head.pug if(is_post()) script(type="application/ld+json"). { "@type": "Article", "headline": "#{page.title}", "author": { "@type": "Person", "name": "#{config.author}" }, "datePublished": "#{page.date}", "dateModified": "#{page.updated}" } else script(type="application/ld+json"). { "@type": "WebSite", "name": "#{config.title}", "url": "#{config.url}" }
四、总结
安全和 SEO 是博客主题容易忽略但非常重要的部分。加密功能保护私密内容,外链拦截降低安全风险(从公安备案到链接安全的完整链路),Sitemap 和结构化数据为搜索引擎提供基础支持。这些功能在 DoraTiger 中均为一体化集成,从第三方插件迁移到主题内部后,适配性和可控性都好了很多。

