本文约 2,644 字,预计阅读 7 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:换域名后图片不显示、主题设置全丢,根源都在替换方式
迁移站点最常见的后遗症:前台能开,但图片全是旧域名;或者 Customizer 里的小工具、菜单、主题选项全部消失。
这两类问题的成因不同但同源——数据库里存着序列化的 PHP 数据,用 SQL 的 REPLACE() 替换会破坏字符串长度标记,PHP 反序列化失败,数据就读不出来了。
wp search-replace 就是为解决这件事而生的:它在替换的同时重新计算序列化字符串的长度。
原理分析:序列化数据为什么会被 REPLACE 破坏
WordPress 的主题选项、小工具、部分插件配置,在数据库里长这样:
a:2:{s:5:"width";i:1200;s:6:"height";i:600;}
注意 s:5:"width" —— 前面的数字是字符串长度。假设把 width 换成 widths(6 个字符),实际内容变了但 s:5 没变,PHP 反序列化时读到的数据就错位,整条数据作废。
换域名时更严重:http://old.com(15 字符)换成 https://new.com(16 字符),长度差 1,凡是序列化数组里存了 URL 的字段全部损坏。
wp search-replace 的做法是:先反序列化,替换,再重新序列化,长度自然正确。
保姆级实操
第一步:先干跑,看影响范围
wp search-replace 'http://old.com' 'https://new.com' --dry-run --report-changed-only
输出会列出每张表、每个字段的替换数量。一定要先看这个报告,如果某张不该被改的表出现了大量替换,说明参数需要调整。
第二步:正式替换(只改核心表)
wp search-replace 'http://old.com' 'https://new.com' \
wp_posts wp_postmeta wp_options wp_comments wp_commentmeta \
--precise
显式列出表名比 --all-tables 更可控。核心表通常只需这几张(表前缀按实际改)。
第三步:处理全站(含插件表)
wp search-replace '//old.com' '//new.com' --all-tables --precise --skip-columns=guid
- 用
//old.com这种形式可以同时覆盖 http 和 https; --all-tables覆盖插件自建表(WooCommerce、多语言插件等);--skip-columns=guid跳过guid字段。guid是文章的永久标识,改了它会导致 RSS 阅读器重复推送,官方也不建议改。
第四步:替换文件路径(本地迁移到服务器时)
wp search-replace '/Users/alex/Local Sites/site/app/public' \
'/var/www/new.com' --all-tables --precise --skip-columns=guid
第五步:刷新并验证
wp rewrite flush
wp cache flush
wp transient delete --all
wp option get home
wp option get siteurl
第六步:逐项验证清单
- 前台首页、内页、分类页各打开一次;
- 后台 → 媒体库,随便点一张图确认 URL 是新域名;
- 后台 → 外观 → 小工具 / 自定义,确认设置还在;
- 用 SQL 检查是否有残留:
wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_content LIKE '%old.com%';"
wp db query "SELECT COUNT(*) FROM wp_postmeta WHERE meta_value LIKE '%old.com%';"
返回 0 才算干净。若 wp_options 里还有残留,多半是插件缓存,清瞬态后再查。
关键参数说明
| 参数 | 作用 | 建议 |
|---|---|---|
--dry-run |
只报告不执行 | 每次都先跑一次 |
--precise |
严格模式,更安全的序列化处理 | 默认加上 |
--all-tables |
所有表 | 迁移场景用,日常替换谨慎 |
--skip-columns=guid |
跳过指定列 | 强烈建议 |
--report-changed-only |
只显示有变化的表 | 输出更清爽 |
--export=file.sql |
导出结果而不写库 | 要保留原库时用 |
--recurse-objects |
递归替换对象内部 | 旧版本需要,新版默认处理 |
常见踩坑与失败案例
- 加了
--skip-columns=guid后部分旧链接还在 —— 属正常,guid不影响前台显示。非要改可单独跑一次不带该参数的替换。 - 替换后站点白屏 —— 多半替换了不该换的表(比如含 PHP 序列化对象但结构特殊的插件表)。用
--export导出审查,或恢复到备份后只替换核心表。 - 案例:某站长用 phpMyAdmin 执行
UPDATE wp_options SET option_value = REPLACE(...),换完域名后整个主题选项面板空白。原因是theme_mods_xxx是序列化数组,长度全错。最后从备份恢复,改用wp search-replace一次成功。 - 大小写与末尾斜杠 ——
http://old.com和http://old.com/是不同字符串。建议都用不带尾斜杠的形式,或用//old.com。 - 多站点(Multisite) —— 需要用
--network并逐个子站替换,主站命令不会自动覆盖子站表。
总结
换域名这件事,技术难度不高但破坏性极大,唯一的正确工具就是 wp search-replace。记住三条纪律:先备份再替换、先干跑再执行、跳过 guid 列。替换完成后用 SQL 复查残留数量,确认归零再切 DNS。这套流程走下来,迁移后的图片丢失、设置清空这类问题基本不会再出现。
