一、为什么要折腾
网站初期功能简单,登录、发文章、改密码全挤在 index.php 一个文件里。文章少的时候还好,文章一多,每次编辑都要翻页找文章,前台用户还能看到管理表单,很不方便。
这次做了三件事:
- 把后台管理独立成 admin/ 目录,前台只展示
- 补全 SEO 三件套(TDK + canonical + Open Graph)
- 文章详情页加侧边栏:最新文章 + 相关阅读
二、从单文件到目录分离
原来的问题
index.php 里混了登录弹窗、发布表单、编辑表单、删除、改密,全部在一起。前台用户打开页面,登录框弹来弹去,体验差也不安全。
现在的结构
bbs/
├── index.php ← 前台首页(只展示)
├── article.php ← 文章详情页
├── config.php ← 数据库连接 + session
├── functions.php ← 工具函数库
├── head.php ← 公共头部(含SEO meta)
├── footer.php ← 公共底部
└── admin/
├── login.php ← 独立登录页
├── index.php ← 后台首页(统计)
├── articles.php ← 文章管理列表
├── edit.php ← 写文章/编辑
└── password.php ← 修改密码
鉴权怎么做的
admin/ 目录下放一个 _auth.php,每个后台页面开头先 require 它:
<?php
require_once __DIR__ . '/../config.php';
if (empty($_SESSION['login'])) {
header('Location: login.php');
exit;
}
没登录直接跳登录页,非常简单。不需要复杂的权限系统,单人站够用了。
三、SEO优化:每篇文章都有独立TDK
原来的问题
所有页面共用一个 title 和 description,搜索引擎抓到的全是重复内容。
现在的方案
在 head.php 里用变量接收各页面传来的 SEO 信息:
<title><?php echo htmlspecialchars($pageTitle ?? "默认标题"); ?></title>
<meta name="description" content="<?php echo htmlspecialchars($metaDesc ?? '默认描述'); ?>">
<meta name="keywords" content="<?php echo htmlspecialchars($metaKeywords ?? '默认关键词'); ?>">
<link rel="canonical" href="<?php echo htmlspecialchars($canonicalUrl ?? ''); ?>">
文章详情页自动从内容提取摘要作为 description:
$metaDesc = getPureSummary($article['content'], 150);
这样每篇文章都有独立的 title、description、keywords、canonical URL,搜索引擎不会判定为重复页面。
发布自动推送 IndexNow
发新文章后自动通知 Bing/Yandex/Naver 收录,不用手动去提交:
$pushUrl = getStaticUrl('article', $newId, 1, [], true);
indexNowPushSingle($pushUrl);
四、侧边栏:没有标签系统怎么做相关文章?
这是最有意思的部分。数据库里没有 tags 表,也没有标签系统,怎么推荐"相关阅读"?
方案:从标题分词匹配
思路很简单:从当前文章标题里提取关键词,去搜其他文章标题里有没有相同的词。
$titleRaw = "易语言+大漠插件(COM)多线程的暂停/恢复/停止";
// 按标点拆分关键词
$kwParts = preg_split('/[\s\+\(\)\/\—\-\|\,,。、::]+/u', $titleRaw);
// 过滤掉1个字的词
$keywords = ['易语言', '大漠插件', '多线程', '暂停', '恢复', '停止'];
// 用这些词去 LIKE 搜其他文章
如果匹配不到 3 篇,自动用最新文章补齐,保证侧边栏不空。
布局
文章正文占 66%,侧边栏占 34%,手机端自动上下堆叠。侧边栏从上到下:
- 关于本站 — 一句话介绍 + 官网链接
- 本站统计 — 文章总数、归档入口、站点地图
- 最新文章 — 5篇最近发布
- 相关阅读 — 标题关键词匹配
五、踩过的坑
1. 编辑器不显示
后台用了 SimpleMDE,但 jQuery 在页面底部加载,初始化代码在它前面执行,导致 $ 未定义,编辑器根本没启动。把 jQuery 提前到头部加载就好了。
2. Bootstrap 栅格不生效
前台用的是 Argon 主题(内置 Bootstrap 4),但自定义 CSS 把 .container 固定了宽度,col-lg-8 + col-lg-4 两栏布局直接掉到下面去了。最后没用 Bootstrap 栅格,直接写 flexbox:
.art-layout { display: flex; gap: 24px; }
.art-main { flex: 0 0 66%; }
.art-side { flex: 0 0 34%; }
@media (max-width: 991px) {
.art-layout { flex-direction: column; }
}
3. 编辑完回不到原页码
从文章列表第 2 页点编辑,保存后点"返回列表"又回到第 1 页。解决办法:编辑链接带上页码参数,保存后跳转时保留页码。
六、总结
一个轻量 PHP BBS,核心就是:
- SQLite 当数据库,零配置
- PDO 预处理 防 SQL 注入
- Parsedown 渲染 Markdown
- 伪静态 .html URL 对 SEO 友好
- 后台/前台分离 安全又好维护
不需要框架,不需要 Composer,几个 PHP 文件就能跑起来。适合个人技术博客和小型 BBS。