把 ShuFeiCat 这个 Typecho 主题折腾上线的过程记录
最近搭了个博客,用的是 Typecho,主题挑了个叫 ShuFeiCat 的。功能宣传得挺全——AI 写作、AI 审核、评论点赞投票、图床直传,看起来像把十几个插件全塞进去了。
我这个人有点轴,没直接装上就开写,而是先把它拉下来翻了遍源码,再一步步部署、加固、上线。这篇就是把这一路的经过记下来,尤其是几个踩过的坑。不一定对你有多大用,但如果正好你也要用这个主题,或者要搭个类似的博客,应该能少走点弯路。
先翻了遍源码
拉下来一看,第一个感觉是:这不只是"主题",更像是一套打包成主题的博客系统。
- 65 个 PHP 文件,三万多行
core/下面塞了 43 个模块- 光一个
functions.php就 195KB,后台配置项 168 个
Typecho 生态里通常要靠十几个插件才能凑齐的东西(AI、图床、邮件、验证码、统计、SEO),它全自己做了。完成度是比一般国产主题高,但换来的是整体很重,而且是一个人维护的(当时 61 个 star、2 个 fork)。
安全上扫了一遍,好坏都有。
好的地方:没有后门,eval、混淆代码、shell 调用一概没有;SQL 全是参数化或者强转;图片代理的 SSRF 防护做得挺细;上传有扩展名白名单。
要留意的几个:
- 后台的 CSRF 只校验 Origin/Referer,没有 token,两个都缺的时候直接放行,还允许子域后缀匹配。前台那些接口(点赞、投票、搜索)倒是用了 session token,是强的。
core/cache/存的是正文的渲染缓存,包括编辑器"免保存预览"生成的草稿。文件名能猜出来(content_文章id.html),等于草稿可能被人直接读走。install.php装完不会自己删,哪天数据库配置一出问题,这个入口能重置整个站。
这三个问题后来上线的时候一个一个都应验了。
SEO 看了下,84 分,有两处我补了下
单独把 core/seo.php(549 行)和 header 的输出读了一遍,给个 84 分吧,在 Typecho 主题里算不错的。
好的地方:分页的 canonical 各自指向自己,没犯那个最常见的错;三套结构化数据都齐;sitemap 是全站的。
但有两个明显的短板,而且都能自己修:
- description 会混进 Markdown 源码。
shufei_seo_trim()只做了strip_tags,没把##、**、链接语法剥掉,所以摘要里可能出现## 前言这种。 - 首页和列表页没有 H1,只有文章页和独立页有。
我就自己改了两处:给 description 加一段 Markdown 清理,首页补一个 sr-only 的 H1(视觉上看不出来,但对 SEO 有用)。后来直接打进部署包里了,上线后验证过是生效的。
打包:做成解压就能用
我的需求是解压到服务器直接装,所以把三样东西拼成了一个 zip:
typecho-1.3.0-shufeicat.zip
├── Typecho 1.3.0 官方版
├── usr/themes/ShuFeiCat/ ← 主题装好了,两处 SEO 补丁也打了
├── robots.txt
└── 安装说明.md有个细节得说下:主题必须放在 usr/themes/ShuFeiCat/ 这一层,错一层的话所有 AJAX 接口直接报错。这个坑太容易踩了。
上线实测,curl 揪出三个问题
部署到服务器之后,我没信"看起来正常",而是用 curl 一条条测。结果真测出三个问题:
install.php 还在。 访问返回 302 而不是 404,响应头里还有 X-Powered-By: PHP/8.5.12,说明是真文件没删。这个最该删,虽然现在有安装锁挡着,但保不齐哪天数据库配置出问题。
robots.txt 域名没改。 我把模板原样传上去了,里面的 Sitemap 还是 example.com,等于提交给搜索引擎的地址是错的。
安全加固没生效。 安全响应头一个都没有,readme.md 能直接访问(返回 200),缓存目录也没封。
补了一段 Nginx 配置
针对第三个问题,给站点配置加了一段。核心是四个 location 加四条安全头,关键在位置——必须放在 location ~ [^/]\.php(/|$) 之前。Nginx 里正则 location 的优先级更高,放后面的话请求就被 .php 那段抢走了,封禁等于没写。
# 屏蔽主题缓存目录(含草稿缓存)
location ~ ^/usr/themes/ShuFeiCat/(core/)?cache/ {
return 403;
}
# 上传目录禁止执行 PHP
location ~ ^/usr/uploads/.*\.php$ {
return 403;
}
# 屏蔽深层敏感文件和备份文件
location ~ ^/(\.user\.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README\.md|\.gitignore|\.DS_Store) {
return 404;
}
location ~ \.(sql|bak|tar\.gz|zip|rar|7z)$ {
return 403;
}
# 安全响应头
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-XSS-Protection "1; mode=block" always;重载之后再测,五条全绿了:
| 检查项 | 之前 | 之后 |
|---|---|---|
| install.php | 302 | 404 |
| readme.md | 200 | 404 |
| 上传目录 PHP | 404 | 403 |
| 缓存目录 | 404 | 403 |
| 安全响应头 | 缺 | 4 条 |
上传目录那个 PHP 从 404 变成 403 是个挺直观的信号,说明正则 location 真的抢在 .php 前面命中了,整段配置不是白写的。
几个坑,单独记一下
PHP 执行时间得提到 120 秒以上。 AI 写作那个端点里没有 set_time_limit(),但它请求 AI 的 curl 超时写的是 60 秒。容器里 PHP 默认 max_execution_time 是 30 秒,模型还没返回,fpm 就先把进程杀了。表现就是点 AI 写作转圈半天然后 502,特别容易误判成接口配错。
SMTP 发信失败是没提示的。 主题内置了 PHPMailer 和测试按钮,但发送失败只写 error_log,前端永远显示成功。所以配完邮箱一定要点测试按钮,真收到邮件才算数。另外云服务器默认封 25 端口,得走 465,密码填的是授权码不是登录密码。
PHP 8.5 反而最省心。 我一开始担心主题声明的 8.3 和实际跑的 8.5 有差异,就把 65 个文件对着 PHP 8.0 到 8.5 的废弃清单扫了一遍,结论是没风险:没有已移除的函数,动态属性都有声明,依赖的扩展都在。主题代码写得很保守,能兼容到 7.2。
面板类型别想当然。 我一度以为是 1Panel,实际是 qpanel。这俩配置路径和操作方式不一样,改错文件就白忙。拿到服务器先确认环境,别急着套模板。
最后
整个过程走下来,其实就几个点:源码先审一遍再部署;搞清楚它把敏感数据存在哪(缓存目录、上传目录);上线后别信"看着正常",拿 curl 逐条验;加固配置要明白 Nginx 的 location 优先级。
主题这东西,管的是页面上那点 on-page 的事,真正能不能被搜到、有没有人看,还是得靠一篇篇写下去。