主题
WordPress 加自定义功能:部署、验证、备份
用 WordPress 做一个"给自己人用"的小站,很快会遇到一个坎:插件市场里的东西都不太对,你要的功能得自己写。
这篇不讲主题开发入门,只讲在真机上安全地改一个跑着的 WordPress ——怎么部署才不丢文件、怎么在不猜密码的情况下测登录态、怎么验证越权没漏、以及改坏了怎么恢复。
前提:容器化,且代码在宿主机上
宿主机 /srv/site/theme-src/ ← 你编辑的地方(git 仓库)
容器内 /var/www/html/wp-content/themes/<theme>/ ← 实际生效的地方关键点:你的源码在宿主机上,主题目录在容器里。 中间靠一个部署脚本同步。
bash
#!/usr/bin/env bash
set -euo pipefail
THEME=mytheme
FILES=(
style.css
functions.php
front-page.php
page-order.php
assets/order.css
assets/order.js
)
for f in "${FILES[@]}"; do
docker cp "theme-src/$f" "wp:/var/www/html/wp-content/themes/$THEME/$f"
done坑一:这个文件清单是硬编码的
上面那个 FILES 数组,是这套流程里最容易出事的地方。
加了一个新模板 page-records.php,只改代码不加清单,结果:部署脚本退出码 0,输出"部署完成",docker cp 也没报错(它只拷贝列出来的文件),但页面上线后是 404。
因为容器里根本没有那个文件——主题明明在,模板不存在,WordPress 就退回到首页或 404。
我在这上面浪费过一轮排查。现在固定做法:加文件的同时,改清单,一次提交。
bash
# 部署后自检:清单里的每个文件都要在容器里存在且大小一致
for f in "${FILES[@]}"; do
a=$(md5sum "theme-src/$f" | cut -d' ' -f1)
b=$(docker exec wp md5sum "/var/www/html/wp-content/themes/$THEME/$f" | cut -d' ' -f1)
[ "$a" = "$b" ] || echo "❌ 不一致: $f"
done坑二:$wpdb->prefix 已经带了前缀
建自定义表时:
php
// ❌ 得到 wl_wl_so_cats(前缀被写了两遍)
$table = $wpdb->prefix . 'wl_so_cats';
// ✅ 得到 wl_so_cats
$table = $wpdb->prefix . 'so_cats';$wpdb->prefix 的值本来就是 wl_ 这种(安装时定的)。再拼一次自定义前缀就重复了。症状是建表成功,但查询全空——因为你查的表名和建的表名不一样。
怎么测「登录状态下的页面」,而不去猜密码
这是我最想推荐的一个技巧。要在命令行里验证"登录用户看到的页面",常见做法是去后台重置密码再造一个测试账号——慢,而且污染数据。
正确做法:让 WordPress 自己给你签一个合法 cookie。
bash
# 在容器里跑 PHP,生成一个合法登录 cookie
docker exec wp wp eval '
$n = "wordpress_logged_in_" . COOKIEHASH;
echo $n . "=" . wp_generate_auth_cookie(1, time() + 3600, "logged_in");
'拿到的字符串直接当 Cookie 头用:
bash
curl -s "http://127.0.0.1:8080/my-page/" \
-H "Cookie: wordpress_logged_in_xxxx=yyyy" \
-H "Host: www.example.com" \
-H "X-Forwarded-Proto: https" \
-o /tmp/page.html -w "%{http_code}\n"那两个 Header 不是可选的
Host:站点是域名配置的,用127.0.0.1访问会被 WordPress 的 canonical 重定向 301 到正式域名。你以为页面挂了,其实是重定向了。X-Forwarded-Proto: https:站点 URL 是https://,缺这个头,WordPress 认为是 http 请求,同样 301。
只带 Cookie 不带你永远只能看到 301,然后误判"登录态无效"。
要进 /wp-admin/ 的话,还需要第二个 cookie:
bash
# wordpress_sec_ 系列的 cookie,同理用 COOKIEHASH 拼
$n2 = "wordpress_sec_" . COOKIEHASH;坑三:wp eval-file 要先放进共享卷
用 docker run --volumes-from wp 这种方式跑 wp-cli 时,那个临时容器和主容器不共享 /tmp。
所以 wp eval-file /tmp/test.php 会报找不到文件。姿势是:
bash
docker cp test.php wp:/var/www/html/test.php
docker exec wp wp eval-file /var/www/html/test.php
docker exec wp rm /var/www/html/test.php # 用完就删,别留在 web 根目录⚠️ 最后那行别忘。/var/www/html/ 是公开目录,测试脚本留在那儿可能直接被人访问。
越权测试:别只看按钮消失了
做"只有某些用户能改"的功能时,前端隐藏按钮不算权限控制。要测的是"带合法请求直接打接口"。
做法:临时建一个最低权限用户(subscriber),用他的 cookie 发请求:
bash
# 1. 建测试用户
docker exec wp wp user create tester3 tester3@example.com --role=subscriber --user_pass=<随机>
# 2. 用他的 cookie,带上真实 nonce,直接 POST 修改请求
curl -s -X POST "http://127.0.0.1:8080/wp-admin/admin-post.php" \
-H "Cookie: $(生成 tester3 的 cookie)" \
-H "Host: www.example.com" -H "X-Forwarded-Proto: https" \
-d "action=my_update_order&order_id=12&status=accepted&_wpnonce=<合法 nonce>"预期结果是"数据没变"。如果状态真被改了,说明你的权限检查写在了前端或者根本没写。
测完记得删测试用户和测试数据:
bash
docker exec wp wp user delete tester3 --yes备份:三件套,缺一不可
改之前先确认备份是能恢复的,不是"应该能恢复"。
我的结构:
| 层 | 位置 | 频率 | 保留 |
|---|---|---|---|
| 本地 | 宿主机 backups/ | 每天 04:00 | 14 份 |
| 异地 | 云挂载/对象存储 | 每天 04:30 | 7 份 |
| 加密 | 随异地包 | 同上 | AES-256 |
异地包里放两类东西:
- 源码包(明文) —— 出事了能立刻看,方便排错
- 完整数据包(AES-256 加密) —— 含数据库、
uploads/、wp-config.php、.env、发信密钥
bash
# 加密(口令单独存一个 600 的文件,不进仓库)
tar czf - db uploads wp-config.php \
| openssl enc -aes-256-cbc -pbkdf2 -salt -pass file:/root/.backup_pass \
> /tmp/site-full.tar.gz.enc⚠️ 云挂载点不能直写。这类挂载(rclone/WebDAV)上,> 重定向会报 Bad file descriptor,tar 直写出来是 0 字节但不报错。必须先在本地 /tmp 打包好,再 cp 过去,并核对 md5:
bash
cp /tmp/site-full.tar.gz.enc /mnt/cloud/backup/
md5sum /tmp/site-full.tar.gz.enc /mnt/cloud/backup/site-full.tar.gz.enc这条坑我单独写了一篇:云挂载为什么不能直写。
一个常被忽略的开关:force_from_email
如果站里装了 SMTP 插件,注意这个选项。打开之后,全站所有邮件(不只是你那个功能发的)都会用同一个发件人——包括评论通知、密码重置、WordPress 自动邮件。
好处是发信域统一、不容易进垃圾箱;坏处是用户收到"密码重置"邮件,发件人却叫「某某点单」,看着像钓鱼。
另外要确认发件地址是否真的能收信。发信域名和收信域名(MX)可以是完全不同的两条链路——我的 love@ 地址只能发不能收,所以邮件模板里必须写"无需回复"或者给一个真实可收的回复地址。
清单
在跑着的站点上加自定义功能,每次走一遍:
- 改源码在宿主机 → 同步更新部署脚本的文件清单
- 部署后逐个文件核对 md5,别信退出码
- 测页面:用
wp eval生成 cookie,带Host+X-Forwarded-Proto - 测权限:用最低权限用户 + 真实 nonce 直接打接口
- 清理测试用户、测试数据、临时 PHP 文件
- 先在本地打包含 DB + uploads + 配置,再
cp到异地并核对 md5
一句话版本:在活着的站点上改东西,最大的风险不是写错代码,是你以为部署生效了。所以每一步都要有"看起来对了不算,得验证"的那一下。