本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/plugins/seo/说明.md。
SEO 优化(seo)插件说明
版本:1.5.0 | 作者:SoSite | 状态:✅ 已启用
功能:搜索引擎优化插件,支持自定义 Meta 标签、robots 指令、canonical、Open Graph、Twitter Card、JSON-LD 结构化数据(含面包屑)、文章元数据、站点地图、robots.txt、llms.txt(AI 爬虫索引)
一、功能简介
- 后台设置页:自定义站点描述/关键词、站点 URL、OG 默认图、自定义 head 代码,以及四个输出开关(robots 指令 / Open Graph / sitemap / llms.txt)
- 全局 head 注入(
layout_head_end):按页面类型输出 <meta name="robots">(低价值页面 noindex;404/410/429 用 noindex,nofollow)<meta name="description">(详情页取正文摘要,列表页/首页取站点描述)<meta name="keywords">(后台配置非空时输出)<link rel="canonical">(自指,分页带?page=N)- Open Graph:
og:type / og:title / og:description / og:url / og:site_name / og:locale / og:image - Twitter Card:
twitter:card / twitter:title / twitter:description / twitter:image(随enable_og门控;页面自带图时用summary_large_image,仅站点图标兜底时用summary) - JSON-LD 结构化数据(每页最多一个块,主节点与面包屑合并进
@graph): - 帖子页
DiscussionForumPosting(含 author / publisher / datePublished / dateModified / commentCount / interactionStatistic) - 博客页
Article(含articleSection/commentCount) - 首页
WebSite(刻意不输出 potentialAction/SearchAction——本站/search未登录会 302,声明不可用的动作会被判错) - 详情页追加
BreadcrumbList(帖子:首页→版块→标题;博客:首页→博客→分类→标题) - 文章元数据(仅详情页):
article:published_time / article:modified_time / article:author、citation_title / citation_author / citation_publication_date - 作者权威信号(B4):JSON-LD 的
author带主页 URL(/u/{id},需user_profile插件启用)与等级署名(Lv.11 · 61 帖;博客行无帖数字段时降级为Lv.11) - 详情页数据复用(
controller_view_before):帖子/博客详情页把控制器已加载的行暂存,供 head 注入复用,省掉一次重查询 - 站点地图:
/sitemap.xml自动生成(首页/论坛/博客/版块/帖子/博客条目,含 lastmod) - robots.txt:
/robots.txt输出爬虫规则(屏蔽后台/API/登录等敏感路径 + Sitemap 指向) - llms.txt(GEO):
/llms.txt输出给大模型/AI 爬虫的站点内容索引,Markdown 格式 - 结构:
# 站点名+> 站点描述(引述块)+## 帖子/## 博客两个分节,每条为- 标题: 摘要 - 候选池:帖子/博客各取
getLatest(100),PHP 侧按「活跃时间」(帖子取last_reply_at、博客取updated_at)统一重排,再全局截断到 100 条——保证是「最近活跃的 100 条」而非「100 帖 + 100 博客」 - 条目文案走
Plugin::oneLine()净化:先去 HTML 与实体、折叠空白,再剔除[ ] ( ) \五个会破坏 Markdown 列表语法的字符(标题含]会提前闭合标题`,导致整份文件解析错位) - 结果渲染成文本后缓存到
plugins/seo/data/llms.txt,TTL 60 分钟;命中缓存时 0 查询 - 开关:
seo_enable_llms(默认开);关闭时输出llms.txt disabled.且不读写缓存文件 - 发现入口:
robots.txt末尾以注释形式声明# llms.txt: https://站点/llms.txt(紧跟Sitemap:行)。用#注释而非Allow:指令 —— 注释对不认识的爬虫无害、对认识的爬虫有用,且与Sitemap:声明对称 - 设置存
settings表(键前缀seo_),无独立数据表
二、目录结构
plugins/seo/
├── plugin.json # 插件清单(hooks: init_after / route_register / admin_route_register / controller_view_before / layout_head_end)
├── Plugin.php # 生命周期 + 设置读写 + SEO 上下文解析(resolveContext/stash/esc)
├── AdminController.php # 后台设置页(index/save,继承 Admin\BaseController)
├── FrontController.php # 前台输出(sitemap/robots/llms)
├── hook/
│ ├── init_after.php # 初始化(历史性能坑:勿每请求写默认值)
│ ├── route_register.php # 前台路由(/sitemap.xml /robots.txt /llms.txt)
│ ├── admin_route_register.php # 后台路由(/seo /seo/save)
│ ├── controller_view_before.php # 暂存 thread/blog 行数据(供 head 注入复用)
│ └── layout_head_end.php # 全局 head 注入(robots / meta / canonical / OG)
├── lang/ # 语言包(zh / en / zh_tw)
├── data/ # 插件生成数据(llms.txt 缓存)+ 目录防护文件
│ ├── .htaccess # Apache:deny all(防缓存文件被 HTTP 直接下载)
│ └── web.config # IIS:hiddenSegments 屏蔽 data 段
├── assets/style.css # 后台设置页样式
└── views/admin.php # 后台设置视图
data/目录里的两个防护文件随包发布(照plugins/medal/data/模板)。Nginx 侧由站点nginx-server.conf的全局正则统一覆盖,不需要单独文件。Plugin::dataDir()在目录/防护文件被误删时会幂等重建,属于自愈兜底,不是唯一防线。
后台入口不在本插件内:由
plugin.json的admin_url+ 核心后台侧边栏渲染,没有admin_sidebar_links钩子文件。
三、数据表
无独立表(配置存 settings 表,键前缀 seo_):
| 键 | 说明 |
|---|---|
seo_meta_description | 站点默认描述(列表页/首页兜底,详情页提取不到正文时也用) |
seo_meta_keywords | 关键词 |
seo_site_url | canonical / sitemap / og:url 的绝对地址前缀;留空回落核心 SITE_URL |
seo_custom_head | 自定义 head 代码(原样输出,值经 Content::decode) |
seo_enable_robots | 是否输出 <meta name="robots"> 指令(默认开) |
seo_enable_og | 是否输出 Open Graph 标签(默认开) |
seo_enable_sitemap | /sitemap.xml 是否可用(默认开) |
seo_enable_llms | /llms.txt 是否可用(默认开) |
seo_og_default_image | OG 兜底图;留空用 /assets/pwa/icon-512.png |
四、Hooks
| Hook | 用途 |
|---|---|
init_after | 初始化(注意性能:勿每请求写默认配置) |
route_register | 前台路由(/sitemap.xml、/robots.txt、/llms.txt) |
admin_route_register | 后台路由(/seo、/seo/save) |
controller_view_before | 详情页暂存 thread / blog 行数据(命中时 head 注入不再重查) |
layout_head_end | 全站 head 注入(robots / description / keywords / canonical / OG / Twitter Card / JSON-LD / 文章元数据) |
五、路由
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /sitemap.xml | XML 站点地图(最近 1000 帖子 + 500 博客 + 版块,含 lastmod W3C 格式) |
| GET | /robots.txt | 爬虫规则(Disallow 敏感路径 + Sitemap: 声明 + # llms.txt: 注释声明) |
| GET | /llms.txt | AI 爬虫内容索引(Markdown,最近活跃 100 条,文件缓存 TTL 60min) |
| GET | /seo | 后台设置页 |
| POST | /seo/save | 保存设置(CSRF,保存后清游客静态页缓存 + llms.txt 缓存) |
六、注意事项
- 性能坑(历史事故):
init_after曾每请求调用Plugin::activate()写 4 个空字符串默认值 → 每页 4 次Settings::update→ 全表扫描 + 写缓存文件,导致全站性能回退。修复:默认配置在插件激活时写入 DB,init_after 勿再重复写默认值 - 性能红线:单插件每请求所有 hook 查询总和 ≤ 5 次。
og:image只做正文正则取首图,不查附件表;详情页经controller_view_before暂存复用,不额外查库 - 布尔开关必须归一:复选框未勾选时浏览器不提交该字段,
AdminController::save()对BOOLEAN_KEYS显式写'0';若存成空串,isEnabled()会当成「未配置」而回落默认值(=开启),开关形同失效 - 游客静态页缓存:首页/版块列表页的 head 会被烘焙进
data/runtime/pages/*.html,保存 SEO 设置后已自动PageCache::invalidate();改本插件 head 输出逻辑后如需立即见效,也需清一次该缓存 - noindex 与 canonical 互斥:命中 noindex 的页面不再输出 canonical(不给「不该被收录的页面」做自指声明)
- JSON-LD 输出约束:必须以
JSON_UNESCAPED_UNICODE | JSON_HEX_TAG编码(JSON_HEX_TAG把<>转成\u003C\u003E,防标题/正文里的</script>提前闭合标签);每页只注入一个 ld+json 块,多个节点合并进@graph - B4 作者 URL 降级:
user_profile插件未启用(或行内user_id为 0)时,author只出name,不出指向 404 的url twitter:card只对位图声明大卡片:has_own_image判的是「页面自带图且是位图」(扩展名限.png / .jpg / .jpeg / .webp / .gif)。博客封面可以是 SVG(实测/blog/16就是),而 Twitter/X 与 Facebook 都不渲染 SVG 分享图——若照样声明summary_large_image,会变成「声明了大卡片却拿不到图」。故 SVG 与其他非位图一律退回summary- llms.txt 缓存失效链路(明确到「谁清、谁不清」):
| 触发 | 是否清缓存 | 理由 |
|---|---|---|
| 后台保存 SEO 设置 | 清(AdminController::save() 调 Plugin::invalidateLlms()) | 站点名/描述/开关变了,缓存文本必须重建 |
| 发新帖 | 不清 | 见下 |
| 删帖 / 编辑 | 不清 | 见下 |
发帖/删帖不清不是因为清理开销大(单次 unlink + 一次钩子 include 约 0.1ms 量级,可忽略),而是命中率问题:发帖/回帖是站内最高频写操作,按「每次写就清一次」的节奏,TTL 60min 的缓存几乎永不命中,等于白建缓存;而每次 AI 爬虫来抓 /llms.txt 仍要付冷缓存成本(Thread::getLatest(100) 含 decorateList 的批量取用户/分类 + Blog::getLatest(100) ≈ 4 次查询)。llms.txt 只是「AI 爬虫的发现入口」,晚 60 分钟收录没有实际损失。另外删帖路径在核心 ThreadController::delete() 里没有任何钩子,要做到「删帖也清」必须往核心加钩子——越线
plugins/seo/data/必须带防护文件:缓存文件是纯文本、可被 HTTP 直接下载,故目录内随包附.htaccess(Apache deny all)+web.config(IIS hiddenSegments),照plugins/medal/data/模板- 站点地图条目数有限制(帖子 1000 / 博客 500),超大站需扩展
- robots.txt 屏蔽路径覆盖:admin/api/login/register/logout/找回密码/消息/个人中心等
- 卸载后
/sitemap.xml、/robots.txt、/llms.txt路由失效(404),meta 注入消失