先在一个外面看不见的站上把改动做完,确认没问题再挪到正式站——流程本身没错,出问题的是「挪」的方式。最常见的做法是把正式站数据库整库导进测试站,改完再整库导回正式站。站上没有订单和真实用户时,这么干看不出毛病;等到有了询盘、订单、会员,导回去那一刻会把这期间产生的数据一起盖掉。
1. 先把要同步的东西分开看
| 东西 | 正式站 → 测试站 | 测试站 → 正式站 |
|---|---|---|
| 主题、子主题、mu-plugins、自建插件 | 整目录复制 | 只带你改过的文件,走 git 或 rsync |
| 第三方插件目录 | 可以复制 | 不要,插件更新在正式站做 |
| 数据库 | 可以,但要改 URL、删掉个人信息 | 不要整库导回 |
| wp-content/uploads | 可以 | 只带测试期间新上传的媒体 |
代码可以双向走,数据基本只能单向。下面所有步骤都是从这条分界线推出来的。
2. 建测试站的几种做法
同主机子目录(example.com/staging):最省事,一份主机一份数据库就够。缺点是如果正式站整站套了 CDN 或者整页缓存,测试站会跟着一起被缓存,改完看不到效果。要做得先把测试目录排除掉。
独立子域名(staging.example.com):多一个 vhost、多一个数据库,但环境干净,最推荐。
本地或容器:改代码够用,但内容类改动看不准,因为库里的数据是旧的。适合主题开发,不适合「把首页重排一遍」这种活。
还有一种环境不是你自己搭的:交付方在正式主机上先给你一个预览站。AumCreate 做 WordPress 模板和代部署(Setup $399),流程是把模板装到你自己的主机上,每一页换成你的内容,先给预览链接、含一轮修改,确认后再上线;材料收齐后 5 个工作日内上线。这条路上不会有「测试库导回正式库」那一步——预览站和正式站是同一套文件、同一个库,区别只是域名还没指过来。域名和主机是你自己付给注册商和主机商的,我们只在交付期间用你给的登录信息。预览期间那个地址会不会被搜到,得跟主机那边确认,别默认它默认是关的。(流程见 Setup)
子域名的具体步骤:
# 1) 正式站导出,排除体积大、测试站又用不上的表(前缀按实际情况改)
wp db export prod.sql \
--exclude_tables=wp_actionscheduler_actions,wp_actionscheduler_logs
# 2) 传到测试站后导入
wp db import prod.sql
# 3) 改站点地址,先 dry-run 看会动多少行
wp search-replace 'https://example.com' 'https://staging.example.com' \
--all-tables-with-prefix --precise --skip-columns=guid --dry-run
# 4) 数字看着合理,去掉 --dry-run 再跑一遍
wp search-replace 'https://example.com' 'https://staging.example.com' \
--all-tables-with-prefix --precise --skip-columns=guid
wp cache flush
wp rewrite flush --hard
几个容易踩的点:
--precise慢一些,但不会漏掉序列化字段里的 URL。少加这一条,页面设置里的链接会留在旧域名上。- 跑完 search-replace,这个库里的正文已经指向测试域名了,它就不适合再导回正式站。测试库只出不进。
- 导完立刻
wp option get home和wp option get siteurl对一下,两个都应该是测试域名。
3. 让测试站别发信、别被搜到、别联网
测试站最容易捅的篓子,是它按正式站的配置给真实客户发了一封邮件,或者把测试流量记进了你的统计报表。
wp-config.php 里加几行:
define( 'WP_ENVIRONMENT_TYPE', 'staging' ); // 让插件知道自己在测试环境
define( 'DISABLE_WP_CRON', true ); // 定时任务别在测试站跑
define( 'WP_HTTP_BLOCK_EXTERNAL', true ); // 断掉对外请求
define( 'WP_ACCESSIBLE_HOSTS', '*.wordpress.org' );
邮件要单独拦一层,因为不少表单插件用的是 wp_mail(),不走普通 HTTP 请求。在 wp-content/mu-plugins/ 下放一个文件,目录不存在就新建:
<?php
/**
* Plugin Name: Staging Guard
* Description: 测试站专用,拦下所有外发邮件,只写日志。
*/
add_filter( 'pre_wp_mail', function ( $short_circuit, $atts ) {
$to = is_array( $atts['to'] ) ? implode( ',', $atts['to'] ) : (string) $atts['to'];
file_put_contents(
WP_CONTENT_DIR . '/staging-mail.log',
sprintf( "[%s] to=%s subject=%s\n", gmdate( 'c' ), $to, $atts['subject'] ?? '' ),
FILE_APPEND | LOCK_EX
);
return true; // 返回非 null 就跳过真正的发送
}, 10, 2 );
放进去之后在测试站提交一次表单,去看 wp-content/staging-mail.log 里有没有记录。有记录说明拦住了;没记录说明还有别的路径在发信,先查清楚再往下做。
再加两样:
auth_basic "staging";
auth_basic_user_file /etc/nginx/.htpasswd;
add_header X-Robots-Tag "noindex, nofollow, noarchive" always;
wp option update blog_public 0
支付类插件在测试站直接停用,或者切到它自带的沙箱。停用最省心——你不会希望测试订单进到真实账里。
4. 哪些数据不能同步
| 数据 | 正式站 → 测试站 | 测试站 → 正式站 | 原因 |
|---|---|---|---|
wp_users / wp_usermeta |
可以,但必须重置密码 | 不要 | 测试站上新建的账号带回去就是后门 |
| 订单、支付、退款、订阅记录 | 只带脱敏快照 | 不要 | 覆盖会打乱对账 |
| 表单条目、询盘 | 可以,先删掉个人信息 | 不要 | 测试站提交的都是假数据 |
wp_options 整表 |
不要整表覆盖 | 不要 | 里面混着站点地址、激活状态、各类密钥 |
cron 这个 option |
不要 | 不要 | 会带一堆过期任务过去 |
| 插件和主题的授权信息 | 不要 | 不要 | 按域名计数的授权会被测试站占掉名额 |
| 统计和站长验证类 option | 置空 | 不要 | 否则测试流量会进你的报表 |
| 缓存、session、transient 表 | 不要 | 不要 | 无意义,还会互相污染 |
uploads 目录 |
可以 | 只带新增媒体 | 整目录双向覆盖会把刚传的图冲掉 |
wp_options 是重灾区。很多人同步时图省事整表覆盖,结果站点地址、固定链接、插件激活状态、授权信息全被打乱。要改配置就用 wp option update 一条一条改,改了什么心里有数。
5. 从测试站把改动挪回正式站
代码:用 git 或者 rsync,只同步你确实改过的目录。先跑一次 dry-run 看清单:
rsync -avn wp-content/themes/my-theme/ \
deploy@prod:/var/www/html/wp-content/themes/my-theme/
确认列出来的文件都是预期的,去掉 -n 再跑。
页面内容:几页的话在正式站手工改最快。页数多就写脚本,用 slug 定位,别用 ID。
<?php
// 保存成 update-home.php,用 wp eval-file update-home.php 执行
$page = get_page_by_path( 'home' );
if ( ! $page ) {
WP_CLI::error( '找不到 home 页面' );
}
wp_update_post( [
'ID' => $page->ID,
'post_content' => file_get_contents( __DIR__ . '/home.html' ),
] );
WP_CLI::success( 'home 已更新,ID=' . $page->ID );
配置项:一条一条来,改完马上读回来确认。
wp option update blogname '新站名'
wp option get blogname
6. 上线前照着对一遍
- [ ] 测试站的
blog_public是 0,basic auth 已开,不登录打不开 - [ ] 提交一次表单,
wp-content/staging-mail.log里有对应记录,真实邮箱没收到信 - [ ] 支付插件处于停用或沙箱状态
- [ ] 主题和插件的文件对比只有预期内的改动
- [ ] 正式站的数据库和
wp-content都做了备份,并且验证过能恢复 - [ ] 上线后自己走一遍:提交一次表单、下一单(沙箱)、用手机打开首页
- [ ] 清掉正式站的页面缓存和 CDN 缓存,再刷新看效果
上线后头一天盯一下错误日志,比事后回滚省事。