手搓论坛: SEO 审计与修复报告

回复:2 查看:39
lsxuehu Lv.2
3小时前 发布 山东

昨天在群里看到有个SEO 审计的skill,集成到自己的智能体试下,下面是报告

结论摘要

本地技术 SEO 审计得分约55/100,发现 6 项缺口;6 项已全部修复并在生产构建上逐条实测通过。 过程中额外揪出3 个会真咬人的工程问题(误报 404、sitemap 被缓存掏空、hook 静默失效),一并修复。

仍未覆盖:Core Web Vitals / 移动端渲染 / 可访问性树 / Agent-UX 分数 —— 这类检查必须跑在公网域名上,原因见「仍未覆盖的限制」。审计环境项值论坛生产实例http://127.0.0.1:3001(node .output/server/index.mjs)站点域名(配置值)https://forum.omhhub.com(通过NUXT_PUBLIC_SITE_URL注入)SEO 运行时%LOCALAPPDATA%claude-seo(Runtime: ready· Chromium 1254)本地放行开关CLAUDE_SEO_LOCAL_TARGETS=localhost:3001,127.0.0.1:3001本报告只覆盖论坛前端站点本身。内容数据来自一个独立的 REST 服务(本文统一称为「上游 API」),其部署地址、存储与鉴权细节不在本报告范围内。必须用生产构建,不能用 dev server:dev 下@nuxtjs/robots会给全站注入noindex, nofollow;且 Windows 上 Nuxt dev 只绑 IPv6[::1],IPv4 请求被拒(WinError 10061)。修复前已通过的部分

这些是审计时确认没问题的,修复过程中保持不变:

检查项结果SSR 首屏有内容passserver-rendered—words_without_js=184,js_shell_marker=falserobots.txt 可达200,indexable,声明 sitemapsitemap 可发现sitemap_discoveryfound=1 / valid=trueX-Robots-Tagindex, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1title / description7 个页面全覆盖未知 URL 返回真 404catch_all_200=false可访问性 HTML 结构real_buttons=4·real_anchors=28·div_onclick_widgets=0·semantic_landmarks=17· 无 label 输入框 0 · 无 aria 输入框 06 项缺口(修复前实测)#缺口修复前实测症状1sitemap 只有静态页只列 5 个静态页(//growth/login/new/register)。板块页/c/[slug]与主题页/t/[id]—— 论坛真正的内容资产 —— 爬虫根本发现不了2canonical 全站为 08 个页面实测 canonical=0 ⇒ 参数 URL、分页 URL 无规范化3参数化 URL 可索引且无 noindex/?keyword=ai→ 200 +index, follow(站内搜索可无限生成重复 URL);/?page=2的 title 与首页一字不差(首页实际无分页,page被忽略 ⇒ 首页的完整重复副本)4软 404/t/18返回 200 但h1=0、jsonld=0;/c/ai返回 200 + 占位 title。帖子/板块不存在时返回200 + 空内容5og / twitter 覆盖不全c/[slug].vue只有 ogTitle;login/register/growth/new完全没有 og;index/t/[id]缺 ogDescription / ogUrl / twitter:title / twitter:image6安全响应头全缺无 CSP / HSTS / X-Frame-Options / X-Content-Type-Options / Referrer-Policy;且X-Powered-By: Nuxt暴露技术栈附带发现:siteUrl是构建期内联的 ——nuxt.config.ts里process.env.FORUM_SITE_URL在nuxi build那一刻求值烤进产物,运行时再设环境变量无效(症状:生产 sitemap 的落回请求 Host)。修复清单六项 SEO 修复01sitemap 纳入动态 URL1上游 API:GET /api/community/sitemap/—— 免鉴权,一次返回全部板块 slug + 主题 id + lastmod2forum 代理层server/api/community-sitemap.get.ts—— 解包上游统一的{code,data,msg}信封3@nuxtjs/sitemap的sources——运行时fetch(不是构建期)不用sitemap.urls:v5 的urls只在构建期resolve(resolveUrls在 module setup 时执行),运行时新增的主题永远进不了 sitemap。用site.url:v5 走 nuxt-site-config-kit,旧的sitemap.siteUrl被静默忽略(症状就是落回请求 Host)。收益:新帖立即进 sitemap;构建期不依赖上游 API 可用。02软 404 → 真 404前端:setResponseStatus(404)只改状态码,页面保留友好提示(UX 不变)。上游 API:详情接口原本用框架自带的「找不到即抛 404 异常」写法,而框架的全局异常处理会把它统一转成code=400+ 一句无意义的英文提示 —— 前端分不出「资源不存在」和「服务异常」。改为显式返回code=404。判定:上游服务不可用 →503;确实不存在 →404。03参数化 URL 规范化URLrobotscanonical/?keyword=ainoindex, followhttps://forum.omhhub.com//?page=2index, followhttps://forum.omhhub.com/

首页只有一个规范地址/。

04og / twitter / canonical 全站补全

新增composables/useForumSeo.ts统一输出:title/description/canonical/og(title, description, url, type, site_name, image)/twitter(card, title, description, image)/robots;app.head.meta再加一层全局兜底。

05安全响应头头值X-Content-Type-OptionsnosniffX-Frame-OptionsSAMEORIGINReferrer-Policystrict-origin-when-cross-originPermissions-Policycamera=(), microphone=(), geolocation=()X-Powered-By已摘除(原先Nuxt)未加 CSP:Nuxt SSR 会输出内联脚本与内联样式,配 CSP 必须配 nonce,配错直接白屏 —— 这个风险不值得在 SEO 改造里顺手引入。06siteUrl 改运行时可配

runtimeConfig.public.siteUrl+NUXT_PUBLIC_SITE_URL⇒ canonical / og:url / sitemap<loc>三者同步生效,换域名不必重新构建。

顺带修掉的三个工程问题AX-Powered-By 摘不掉 —— hook 写错了位置

render:response是Nitrohook:

写在nuxt.config.ts顶层hooks(Nuxthook)里 → 永远不调用,死代码;写在nuxt.config.ts的nitro.hooks里 →也不会进产物(实测 build6 产物.output/server/chunks/_/nitro.mjs里根本没有这段代码);靠routeRules[‘/**’].headers覆盖也不行 —— Nuxt renderer 返回的headers是在 routeRules之后写出去的。

最终方案:server/plugins/remove-powered-by.ts的defineNitroPlugin(Nitro 侧唯一稳定的注册入口),已进产物并生效。

B上游服务一抖,有效板块页误报 404

getCategories原来在code !== 2000时静默返回[]⇒ 板块页判成「板块不存在」⇒对爬虫返回 404⇒ 页面被踢出索引。实测 build4 上/c/announce在上游服务不可用时真的返回过 404。

正解:前端解包后判业务码,不成功就抛,走 503 分支。getTopics同理 —— 原来上游服务挂时首页会显示「还没有主题」,把服务故障伪装成业务空态。

Csitemap 被掏空(且被缓存 10 分钟)

/sitemap.xml路由自带600s SWR 缓存(”swr”: 600, “maxAge”: 600),动态源一旦返回空,这个空 sitemap 会被缓存 10 分钟 —— 正好是爬虫抓取窗口,板块页/主题页集体从 sitemap 消失。

实测:上游服务不可用时 sitemap 从 17 条掉到1 条(只剩首页)。

正解:代理端点保留「最后一次成功结果」,上游失败时回退旧值。

修复后验证(生产构建 build9 实测)状态码路径状态码耗时/2007818 ms/login20061 ms/register20076 ms/growth20061 ms/new20090 ms/c/announce2001828 ms/c/agent2002187 ms/c/nonexistent-slug404✅822 ms/t/192004517 ms/t/999999404✅3853 ms/nonexistent-page404✅47 ms/sitemap.xml200942 ms/robots.txt20026 ms首屏 2–8 秒是远端数据源延迟导致的(上游列表接口实测 0.8–6 s),不是论坛侧问题。响应头(首页,最终产物原样)Connection : keep-alive Content-Length : 52855 Content-Type : text/html;charset=utf-8 Date : Tue, 10 Nov 2026 13:15:36 GMT Keep-Alive : timeout=5 Permissions-Policy : camera=(), microphone=(), geolocation=() Referrer-Policy : strict-origin-when-cross-origin X-Content-Type-Options : nosniff X-Frame-Options : SAMEORIGIN X-Robots-Tag : index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1

X-Powered-By: Nuxt已消失(修复前存在)。404 页同样带全部 4 个安全头。

sitemap.xml

loc=17·lastmod=16· 2605 bytes · 全部https://forum.omhhub.com:

/ 首页 /c/agent /c/announce /c/arena /c/feedback /c/learn /c/lounge /c/memory-hub /c/newbie (8 个板块页) /t/11 /t/12 /t/13 /t/14 /t/16 /t/17 /t/18 /t/19 (8 个主题页)

noindex页面(/login/register/growth/new)与/dev/**已排除在外。

robots.txt# START nuxt-robots (indexable) User-agent: * Sitemap: https://forum.omhhub.com/sitemap.xml # END nuxt-robots

AI 爬虫命名组保持现状全放行(OAI-SearchBot/Claude-SearchBot/PerplexityBot决定 AI 搜索引用;禁训练爬虫不改善搜索收录)。

head 标签矩阵(10 个页面逐项核对)页面titlecanonicalrobotsogtwitterJSON-LD/✓…/index✓✓0/login✓…/loginnoindex✓✓0/register✓…/registernoindex✓✓0/growth✓…/growthnoindex✓✓0/new✓…/newnoindex✓✓0/c/announce✓…/c/announceindex✓✓0/c/agent✓…/c/agentindex✓✓0/t/19✓…/t/19index✓✓1/?keyword=ai✓…/noindex✓✓0/?page=2✓…/index✓✓0

主题页 JSON-LD 实样:

{“@context”:”https://schema.org”,”@type”:”DiscussionForumPosting”, “headline”:”周末爬山,大家去哪”, “url”:”https://forum.omhhub.com/t/19″, “mainEntityOfPage”:”https://forum.omhhub.com/t/19″, “datePublished”:”2026-11-08 23:43:06″, “articleBody”:”济南周边周末适合登高。……”, “publisher”:{“@type”:”Organization”,”name”:”OMHHub · AI社区”}}运行时换域名对照实验

另起 3002 端口,只设一个环境变量NUXT_PUBLIC_SITE_URL=https://seo-test.example.com(构建时未设该变量):

canonical → https://seo-test.example.com/login og:url → https://seo-test.example.com/login sitemap → https://seo-test.example.com/ · /c/agent · /c/announce

⇒ 换域名不必重新构建,且 canonical / og:url / sitemap<loc>三者严格同步。

环境变量名必须是NUXT_PUBLIC_*。FORUM_SITE_URL只在nuxi build那一刻被求值烤进产物,运行时再设无效。SEO 插件复测脚本结果sitemap_discovery.pyfound=1 ·kind=urlset·status_code=200·valid=trueagentic_check.pyserver-rendered: pass(words_without_js=184 / js_shell_marker=false)·robots-reachable: pass(200 / groups=1)fetch_page.py抓取本地正常(需CLAUDE_SEO_LOCAL_TARGETS)浏览器实测页面ConsoleNetwork(failed / 4xx / 5xx)渲染/0 条0板块 / 主题 / 热帖 / 签到卡 全部正常/c/announce0 条—板块头 + 主题列表正常,页脚只剩版权行/t/190 条0正文 / 楼层 / 侧栏正常/t/999999——友好文案「无法打开这个帖子 / 帖子不存在或已删除 / 返回首页」,HTTP404上游服务不可用时的行为(隔离实测)

另起 3003 端口,用NUXT_PUBLIC_API_BASE指向一个没人监听的端口(http://127.0.0.1:59999)模拟上游服务完全不可用 —— 不动真实服务:

路径HTTP说明/c/announce503板块存在,但上游挂了 ⇒ 503。修复前这里会误报 404/c/nonexistent-slug503同上 —— 修复前getCategories静默返回[],被当成「板块不存在」⇒ 404/t/19503主题存在,上游挂了 ⇒ 503(主题页修复前也是 503,它本来就按code区分了)/t/999999503同上(修复前也是 503)/200首页页内显示「主题加载失败 / 板块加载失败」错误态,首页本身仍是 200⇒ 爬虫在后台抖动期间拿到的是503(临时不可用,会重试),而不是404(判定为不存在,直接踢出索引)。

这次修复实际改变行为的是板块页:修复前getCategories在上游异常时静默返回[],c/[slug].vue判「列表已加载但没这个 slug ⇒ 404」⇒有效板块页对爬虫返回 404。实测 build4 上/c/announce在上游不可用时真的返回过 404(而/c/announce是存在的板块)。主题页因为getTopic本来就按上游code抛错,行为没变。

仍未覆盖的限制①渲染类检查做不了(需要公网域名)

Core Web Vitals、移动端渲染、可访问性树、Agent-UX 分数 —— 插件的 Playwright 对本地目标必然失败(route handler 只放行公网 IP,顶层导航被 abort →Page.goto: net::ERR_FAILED)。

forum.omhhub.com目前未解析。要跑这批检查就得先把站点挂到公网,涉及对外暴露。

②上游数据源偶发断连

上游服务在运行中会返回「连接被意外关闭」这类底层异常(HTTP 仍是 200,但业务码表示失败)。本轮实测触发过 3 次:

上游返回code=4000+ 该异常原文;forum 代理端点回退「最后成功结果」,sitemap 保持 17 条(不塌成 1 条);板块页 / 主题页在这几秒内返回503(而不是误报 404)—— 与上面的隔离实测一致。

这是环境问题,不是代码问题,但生产部署前建议给上游数据源加连接保活或换更稳定的实例。

③其他未动的项(本轮范围外)本地缓存服务密码未配对,框架级缓存整体不可用(已绕开)管理端的实时推送通道在开发服务器下不可用(需 WebSocket 支撑)生产环境里的演示账号尚未核查是否需要停用论坛「每日签到」卡的亮紫渐变「登录签到」按钮未收敛板块管理「允许匿名」「发帖需审」两列仍显示true/false文本改动文件清单

forum/(独立 Nuxt 站点)

文件改动nuxt.config.tsruntimeConfig.public.siteUrl·site.url·sitemap.sources/exclude· 全局 og 兜底 · Nitro 安全头composables/useForumSeo.ts新建—— title / description / canonical / og / twitter / robots 统一输出composables/useCommunityApi.tsgetCategories/getTopics判业务码抛错,不再静默返回空数组server/api/community-sitemap.get.ts新建—— sitemap 动态源代理 + 「最后成功结果」回退server/plugins/remove-powered-by.ts新建—— Nitro plugin 摘x-powered-bypages/index.vue搜索页noindex+ canonical 归一pages/t/[id].vue真 404 / 503 判定 · JSON-LD 补url/mainEntityOfPagepages/c/[slug].vue真 404 / 503 判定pages/login.vueregister.vuegrowth.vuenew.vuenoindex, follow+ 完整 og / twitterapp.vue页脚精简(只留版权)+ 品牌 logo 衬底主题化components/ForumHeader.vue品牌 logo 白框主题化(亮色裸放 / 暗色保留衬底)ROADMAP.mdSEO 条目按实际完成情况收口上游 API 侧的配套改动(新增 sitemap 数据源端点、详情接口改显式 404)不列入本报告。复现命令# 论坛生产构建(dev server 在跑时必须加 IGNORE_LOCK) Set-Location “$env:USERPROFILEpathtoomhhubforum” $env:NUXT_IGNORE_LOCK = “1” npx nuxi build # → Build complete! / EXITCODE=0 # 起生产服务(SEO 审计专用,端口 3001) $env:PORT = “3001”; $env:NITRO_HOST = “127.0.0.1” $env:NUXT_PUBLIC_SITE_URL = “https://forum.omhhub.com” $env:NUXT_PUBLIC_API_BASE = “<上游 API 基址>” # 运行时覆盖名,OMH_API_BASE 无效 node “.output/server/index.mjs” # SEO 插件复测 $env:CLAUDE_SEO_LOCAL_TARGETS = “localhost:3001,127.0.0.1:3001” python “/scripts/claude-seo.py” run sitemap_discovery.py “http://127.0.0.1:3001” –json python “/scripts/claude-seo.py” run agentic_check.py “http://127.0.0.1:3001/” –json # 站点自检 Invoke-WebRequest -Uri “http://127.0.0.1:3001/sitemap.xml” -UseBasicParsing -TimeoutSec 90 Invoke-WebRequest -Uri “http://127.0.0.1:3001/robots.txt” -UseBasicParsing -TimeoutSec 60部署前必须落位的变量变量值说明NUXT_PUBLIC_SITE_URLhttps://forum.omhhub.com域名必须是这个变量名;驱动 canonical / og:url / sitemapNUXT_PUBLIC_API_BASE上游 API 基址驱动 SSR 取数FORUM_OPEN_REGISTERTrue上游服务的自助注册开关(上游进程环境变量),关闭则回退「必须邀请码」OMH_API_BASE和FORUM_SITE_URL都是构建期变量——nuxt.config.ts里读process.env.X,求值发生在nuxi build那一刻,运行时再设完全无效。
实测证据:3003 实例只设OMH_API_BASE=http://127.0.0.1:59999(运行时)时,页面仍连真实上游返回 200/404;改用NUXT_PUBLIC_API_BASE才真的断开(返回 503)。
⇒ Nuxt 的运行时覆盖只认NUXT_PUBLIC_*命名。

打赏
点赞
收藏
分享
暂无签名