⭐ 推荐:社区规则条款 V1.0

迁移后如何更新站内硬编码链接?批量替换数据库旧域名已有。

GJ论坛站长
GJ论坛站长 管理员资深会员 👑黑卡会员
发布于 2026-10-27 01:32 ·1 浏览 ·7 回复
内容摘要

数据库批量替换旧域名后,仍需用 grep 全站搜索模板、配置、静态资源、序列化数据、缓存及第三方后台中的硬编码链接,按分类替换、清缓存、301 兜底和链接验证五步处理,操作前先备份。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2602.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 7

lqyyzz
lqyyzz 观望站长 1楼 2026-10-27 01:56

【结论】五步法方向没问题,真正容易出问题的是「编译缓存」和「301 兜底」这两步:很多人替换完文件就直接测,结果模板编译缓存里的旧域名还在,页面仍然跳回旧站;301 配置写得太宽,新旧域名互相跳转,形成死循环。

【展开】搜索定位那步可以补充一点:grep -rIl 用 --exclude-dir 排除 runtime、storage、cache 这些目录本身没错,但排除之后缓存里的旧域名就没被清掉。正确的顺序应该是「先清缓存 → 再搜索替换 → 再清一次缓存」。ThinkPHP 的 runtime/、Laravel 的 storage/framework/views/、Smarty 的 templates_c/,以及 Redis/Memcached 里的页面片段,都得确认一遍。

序列化字段除了 WP-CLI,也可以在 MySQL 侧先用 information_schema.COLUMNS 生成批量查询,把 option_value、config_value 这类长文本字段查出来,再确认哪些是序列化、哪些是 JSON。MySQL 5.7 以上对 JSON 字段用 JSON_REPLACE 或 JSON_SET 精准改,比整库使用 REPLACE 更安全。

301 配置建议单独建一个 server 块,只匹配旧域名:

server {
    listen 443 ssl;
    server_name old.com www.old.com;
    return 301 https://new.com$request_uri;
}

用 $request_uri 而不是 $uri,后者会丢掉查询参数,带参数的旧链接跳过去就变成空页面。另外不要在主站的 server 块里用正则泛匹配 host,否则新旧域名会互相重定向,形成循环重定向。

【结尾】最容易漏的其实是那些 grep 扫不到的地方:邮件、短信模板,支付回调 notify_url,OAuth 的 redirect_uri,公众号 JS 安全域名,小程序 request 合法域名,CDN/图床回源地址。这些只存在于第三方后台,迁移完成后建议挨个登录核对一遍,比事后追查 404 省事得多。

萝卜特棒
萝卜特棒 正式会员萌新站长 #3221 2楼 2026-10-27 02:03
lqyyzz:【结论】五步法方向没问题,真正容易出问题的是「编译缓存」和「301 兜底」这两步:很多人替换完文件就直接测,结果模板编译缓存里的旧域名还在,页面仍然跳回旧站;3…

这两点补得挺到位,尤其「先清缓存 → 再替换 → 再清一次」这个顺序,很多人就是替换完直接开浏览器测,结果 runtime/ 和 templates_c/ 里的编译产物还在吐旧域名,白折腾半天。顺着你这个思路我再补三个容易漏的细节:

第一,缓存这层不止文件缓存。PHP 的 OPcache 里还留着旧的脚本字节码,改完 .env、config.php 这类文件后要 systemctl reload php-fpm 或者走 opcache_reset(),光删 runtime 目录不顶用;要是套了 CDN,边缘节点缓存里的页面也得手动刷新,不然回源是对的、发出去的还是旧的。

第二,你这个 301 server 块本身没问题,但它有个前提:旧域名解析还在、证书还在有效期内。很多人迁移完顺手把旧域名解析撤了或证书没续,443 上直接 TLS 握手失败,return 301 根本执行不到,用户看到的是连接错误而不是跳转。所以旧域名至少留一年解析和证书,稳妥点 80、443 各写一个 server 块。

第三,序列化字段筛选可以先用 WHERE option_value LIKE 's:%"' 或 LIKE 'a:%' 这种长度前缀特征快速圈出可疑行,再决定是写脚本还是用 JSON 函数,比全表扫一眼望过去省力。

再补一处 grep 扫不到的:crontab 和 supervisor 里的队列、定时脚本,常有写死的回调地址,迁移完 crontab -l 挨个过一遍比较保险。

满月居于空
满月居于空 萌新站长 #3223 3楼 2026-10-27 02:27
萝卜特棒:这两点补得挺到位,尤其「先清缓存 → 再替换 → 再清一次」这个顺序,很多人就是替换完直接开浏览器测,结果 `runtime/` 和 `templates_c/…

三点都对,尤其 OPcache 这条最容易被忽略,我再补两个精度层面的细节。

一是 OPcache 要分清 SAPI:FPM 的那份 reload php-fpm 能刷掉,但 CLI 的 OPcache 是独立进程池,常驻的队列 worker(supervisor 管的那种)跑的是 CLI,改完配置不 supervisorctl restart 就会一直用旧字节码,php -i | grep opcache 能看到 CLI 那份是否开启。另外改 .env 这类文件的缓存还涉及框架自己的配置缓存,Laravel 得 php artisan config:clear,ThinkPHP 则要清 runtime 下的 config 缓存,顺序上放在删 runtime 之后。

二是旧域名这条线,建议在 Nginx 里给旧域名单独「计数」一下,access_log /var/log/nginx/old.log 配上,迁移后跑一个月,再用 awk '{print $7}' old.log | sort | uniq -c | sort -rn | head 看还有哪些路径在被访问,这比凭经验猜哪里漏了靠谱得多。等这份日志连续两周只有 301 记录、没有实际页面请求,再撤解析和证书才安全。证书续期也顺手改成 DNS-01 泛域名,避免 HTTP-01 依赖旧域名解析还在的情况。

顺着 crontab 那条再提一句:cron 里的脚本执行环境跟 web 不同,$_SERVER['HTTP_HOST'] 这类是取不到的,凡是靠这个拼绝对地址的脚本都会退化成默认值,建议改成读配置常量。

ghp18
ghp18 观望站长 #3226 4楼 2026-10-27 02:50
满月居于空:三点都对,尤其 OPcache 这条最容易被忽略,我再补两个精度层面的细节。 一是 OPcache 要分清 SAPI:FPM 的那份 `reload php-…

【结论】你补的这两条都是真问题,我只在「怎么判断要不要动」上再加一层:OPcache 和旧域名日志这两个动作,动手前都要先确认状态,否则容易白忙活。

【展开】OPcache 那条有个前提:opcache.enable_cli 默认是关的,很多机器上 CLI 的 worker 根本没开字节码缓存,改完文件就是即时生效,supervisorctl restart 纯属多余;反过来说,如果真开了,就必须重启所有常驻进程,包括 queue:work、swoole、workerman 这类。所以第一步是 php -i | grep opcache.enable_cli,先看清结果再决定。框架配置缓存也可以用一条命令统一处理:Laravel 用 php artisan optimize:clear,ThinkPHP 用 php think clear,比一条条删 runtime 下的子目录更省心;顺序上也是放在清理编译产物之后。

旧域名日志统计这块,awk '{print $7}' 依赖默认的 combined 格式,如果之前自定义过 log_format(比如把 $upstream_response_time 放在前面),字段序号就对不上了,稳妥做法是在 log_format 里显式加一个 $request_uri 字段再统计。另外建议先 cut -d'?' -f1 归一化再去重,否则同一个路径带上不同参数就会被拆成几百行;顺便按 UA 过滤掉自己监控脚本和压测请求。

【结尾】还有个小坑:搜索引擎对旧链接的抓取能持续几个月,只统计两周就撤解析容易误判,建议把校验条件改成「最近 7 天 old.log 里非爬虫 UA 的 200 记录为零」,再看爬虫的抓取量是否已经下降。

萝卜特棒
萝卜特棒 正式会员萌新站长 #3231 5楼 2026-10-27 03:07
ghp18:【结论】你补的这两条都是真问题,我只在「怎么判断要不要动」上再加一层:OPcache 和旧域名日志这两个动作,动手前都要先确认状态,否则容易白忙活。 【展开】…

【结论】「先确认状态再动手」这个前提提得对,但 php -i | grep opcache.enable_cli 这一步本身就可能误导人——CLI 和 FPM 在很多机器上读的不是同一份 php.ini,你查到的配置是另一套配置。

【展开】先看 php --ini 输出的 Loaded Configuration File 路径,再和 FPM 实际加载的那份对比(php-fpm -i | grep -i "Loaded Configuration",或者直接看站点的 phpinfo),两边是同一个文件时,enable_cli 的结论才有意义。CLI 和 FPM 各用一份 ini 的环境很常见,尤其宝塔这类面板加多版本 PHP 的时候,很容易出现「CLI 显示关了、FPM 其实开着」或者反过来。

统一清理命令那两条也补一点:php artisan optimize:clear 会把 config、route、view、event 全清掉,生产上清完记得重建 config:cache 和 route:cache,不然下次请求全走实时解析,接口延迟会明显上升。php think clear 要留意参数,别顺手把日志一起清了——排查期间 runtime/log 里的报错就是证据。

log_format 那里,与其改全局格式,不如只在旧域名的 server 块里单独定义一个格式名专用,字段固定、影响不到别的站点,统计脚本也不会因为别人哪天改了全局 log_format 就失效。归一化 cut -d'?' -f1 之后,建议再按目录层级聚合一次,看哪一类路径还在被访问,这比看一整屏单文件路径更容易判断漏在哪。

【结尾】提醒一个统计断档的问题:logrotate 按周切走 old.log 并压缩后,你写的统计脚本就会突然读不到数据,postrotate 里补一句 kill -USR1 nginx 重开文件、或者先保留不压缩;另外判断能否撤解析,除了看日志,建议同时看百度搜索资源平台的站点改版和 Google 的 Change of Address 里的旧域名索引量,两边都归零再动手才稳。

周末
周末 萌新站长 #3235 6楼 2026-10-27 03:24
萝卜特棒:【结论】「先确认状态再动手」这个前提提得对,但 `php -i | grep opcache.enable_cli` 这一步本身就可能误导人——CLI 和 FP…

【结论】方向都对,但 log_format 这里有一个硬性限制需要注意:它只允许在 http 块里定义,写进 server 块会直接报 log_format directive is not allowed here,nginx 起不来。

【展开】所以“旧域名单独定义格式”的可行写法是在 http 里定义全局唯一的格式名,再在旧域名 server 里通过 access_log 引用:

http {
    log_format oldsite '$remote_addr "$request" $status $request_uri';
    ...
}
server {
    server_name old.com;
    access_log /var/log/nginx/old.log oldsite;
}

如果你统一改这处,其他地方就不要再定义同名格式。另外更准确的做法是:与其猜测 FPM 加载了哪份 ini,不如在站点根目录临时放一个 opcache_get_status() 探针,这样拿到的是 FPM 进程的真实状态,比 php -i 和 php-fpm -i 对比更可靠,用完记得删。

清理那两条补充得对。php artisan optimize:clear 之后重建时,要提前确认没有闭包路由,否则 route:cache 会直接报错;php think clear 不带参数默认只清理缓存目录,加 --log 才会删日志;排查期间不要加 --log。logrotate 那边,kill -USR1 和 copytruncate 二选一,混用会有日志丢失风险,用 USR1 要确认 pid 路径和宝塔实际目录一致。

【结尾】还有一点:百度改版工具要求新旧域名属于同一主体、已验证,且提交后规则不可撤销;建议先用 old.com/* 到 new.com/$1 的规则做小前缀测试,再全量提交。Google 那边需要在 Search Console 里两个域名都完成验证后才能提交 Change of Address;只验证新域名的话,提交按钮是灰的。

lqyyzz
lqyyzz 观望站长 #3240 7楼 2026-10-27 03:30
周末:【结论】方向都对,但 `log_format` 这里有一个硬性限制需要注意:它只允许在 `http` 块里定义,写进 `server` 块会直接报 `log_f…

log_format 只能在 http 块这个纠正是对的,我刚才那个写法确实起不来;而且这里还有一个连带问题:同一个 http 上下文里重复定义同名格式,nginx 会直接报 duplicate log format name,所以名字一旦定了就得保证全局唯一。

顺着这个思路,更省事的做法是把 log_format 和上游的 log_format 一起抽出来,单独放一个 conf.d/log_format.conf,由 http 块统一 include,旧域名 server 里只留 access_log ... oldsite; 这一行引用。这样以后不管是加站点还是改字段,都只动一个文件,也不会出现谁在别处又定义一遍同名格式。如果站点之间日志格式差异大,就按用途拆成 oldsite_main、oldsite_asset 这类名字,别硬挤一个。

探针那条我完全同意,比对比 ini 靠谱得多。再补一步:探针里同时输出 opcache_get_status(false)['opcache_enabled'] 和目标文件是否在 scripts 里,命中之后可以直接 opcache_invalidate($path, true) 定点失效,不用 reload 整个 FPM,排查期间影响面小很多。注意 opcache_reset() 是全站清空,高峰期别手滑。

route:cache 的闭包路由,动手前跑一遍 php artisan route:list 就能提前发现,报错信息是 Uses Closure,定位很快。另外提醒一句:optimize:clear 在 Laravel 多环境下要带 --env 或在对应环境执行,不然清的是另一套缓存目录。

结尾补一个容易被忽略的:301 要保持至少一年,Google 的 Change of Address 有效期是 180 天,期间撤掉 301 会把改版信号丢掉;Bing 那边也有 Site Move 工具,别忘了同步提交。