搜索全站
网站迁移 7 分钟阅读

WP-CLI search-replace 批量替换域名:安全迁移的关键命令

换域名或迁移站点时,直接执行 SQL REPLACE 会破坏序列化数据。本文讲清为什么必须用 wp search-replace,详解 --dry-run、--precise、--all-tables、--skip-columns 等参数的用法与取舍,给出完整迁移流程与替换后验证清单,附真实事故案例。

U
UFO_PF 2024-09-13 · 阅读 7 分钟
WP 网站迁移
阅读提示
本文约 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

第六步:逐项验证清单

  1. 前台首页、内页、分类页各打开一次;
  2. 后台 → 媒体库,随便点一张图确认 URL 是新域名;
  3. 后台 → 外观 → 小工具 / 自定义,确认设置还在;
  4. 用 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 递归替换对象内部 旧版本需要,新版默认处理

常见踩坑与失败案例

  1. 加了 --skip-columns=guid 后部分旧链接还在 —— 属正常,guid 不影响前台显示。非要改可单独跑一次不带该参数的替换。
  2. 替换后站点白屏 —— 多半替换了不该换的表(比如含 PHP 序列化对象但结构特殊的插件表)。用 --export 导出审查,或恢复到备份后只替换核心表。
  3. 案例:某站长用 phpMyAdmin 执行 UPDATE wp_options SET option_value = REPLACE(...),换完域名后整个主题选项面板空白。原因是 theme_mods_xxx 是序列化数组,长度全错。最后从备份恢复,改用 wp search-replace 一次成功。
  4. 大小写与末尾斜杠 —— http://old.com 和 http://old.com/ 是不同字符串。建议都用不带尾斜杠的形式,或用 //old.com。
  5. 多站点(Multisite) —— 需要用 --network 并逐个子站替换,主站命令不会自动覆盖子站表。

总结

换域名这件事,技术难度不高但破坏性极大,唯一的正确工具就是 wp search-replace。记住三条纪律:先备份再替换、先干跑再执行、跳过 guid 列。替换完成后用 SQL 复查残留数量,确认归零再切 DNS。这套流程走下来,迁移后的图片丢失、设置清空这类问题基本不会再出现。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。