一、为什么要折腾

网站初期功能简单,登录、发文章、改密码全挤在 index.php 一个文件里。文章少的时候还好,文章一多,每次编辑都要翻页找文章,前台用户还能看到管理表单,很不方便。

这次做了三件事:

  1. 把后台管理独立成 admin/ 目录,前台只展示
  2. 补全 SEO 三件套(TDK + canonical + Open Graph)
  3. 文章详情页加侧边栏:最新文章 + 相关阅读

二、从单文件到目录分离

原来的问题

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。