A Tour of Go 简体中文站正式上线后,我很快注意到了一个之前一直没有优先处理的问题:生产域名和 Tour 自身的路径出现了语义重复。
原来的正式域名是:
https://go-tour.shuijingwanwq.com
而 A Tour of Go 本身仍然沿用官方的 /tour/ 路径,因此实际课程页面会变成:
https://go-tour.shuijingwanwq.com/tour/welcome/1
也就是说,域名里的 go-tour 和路径中的 /tour/ 重复出现。
其实在正式上线前一晚,我已经专门分析过这个问题。
当时最直接的想法并不是更换域名,而是:
能不能直接把 URL 中的
/tour/去掉?
如果能够改成:
https://go-tour.shuijingwanwq.com/welcome/1
从表面上看,URL 会简洁很多。
但继续分析后发现,/tour/ 并不是一个可以随手删除的装饰性前缀。它已经和当前 Tour 的路由、页面路径、静态资源、模板结构、浏览器验收以及现有发布方式形成了比较深的关联。
如果为了消除这一层重复而直接去掉 /tour/,就需要重新审视和修改一整套已经完成验证的路径逻辑。
而项目当时才刚刚完成:
- 103 个课程页面全部就绪;
- 公共 UI 本地化;
- 浏览器最终验收;
- 生产发布包;
- EdgeOne 与 Nginx 部署;
- 远程 Go Playground 运行链路。
为了让 URL 少一个 /tour/,去重新扩大程序和验证范围,性价比并不高。
因此,当晚最终放弃了“删除 /tour/”这个方案。
到了第二天,我换了一个方向思考:
既然
/tour/暂时不值得动,那么能不能把域名本身从go-tour调整成一个更宽泛的名称?
这时 go-dev 就成为了一个比较自然的候选。
如果改成:
https://go-dev.shuijingwanwq.com/tour
原来的 go-tour.../tour/... 重复问题自然消失,同时又不需要修改已经稳定下来的 Tour 路由和页面结构。
更重要的是,这还解决了另一个长期问题。
如果 A Tour of Go 后续能够获得一定流量,我可能还会继续翻译 go.dev 中其他有价值的官方内容。
这时 go-tour 作为站点级域名就显得有些窄,而:
go-dev.shuijingwanwq.com
更适合作为一个未来可能承载多个 Go 开发文档目录的长期入口。
最终,我决定把正式生产域名从:
go-tour.shuijingwanwq.com
迁移到:
go-dev.shuijingwanwq.com
新的 A Tour of Go 正式入口变成:
https://go-dev.shuijingwanwq.com/tour
这次调整因此并不只是为了让 URL 看起来更简洁,而是在不破坏现有 /tour/ 路径结构的前提下,用更小的改动解决域名重复,同时给未来扩展留下空间。
本文记录这次从 go-tour 到 go-dev 的完整生产域名迁移过程,包括 Cloudflare DNS、腾讯云 EdgeOne、HTTPS、301 重定向、OneinStack、Nginx、Let’s Encrypt,以及最终的源站清理和验收。
一、迁移原则:只更换公网入口,不重构内部服务
这次迁移首先确定了一个原则:
公网域名需要调整,但已经稳定运行的内部项目和服务名称没有必要跟着修改。
迁移前:
https://go-tour.shuijingwanwq.com/tour
迁移后:
https://go-dev.shuijingwanwq.com/tour
但是下面这些内部名称全部继续保持不变:
- GitHub 仓库:
go-tour-i18n - Go module path:
github.com/shuijingwan/go-tour-i18n - systemd 服务:
go-tour.service - 服务监听地址:
127.0.0.1:3999 - 生产发布目录:
/data/go-tour/
也就是说,这次真正调整的是:
公网域名 → CDN → Nginx 虚拟主机
而不是把整个项目从 go-tour 全部重命名成 go-dev。
这样可以显著缩小迁移范围,同时避免为了名称统一而给已经稳定的生产服务增加风险。

go-dev.shuijingwanwq.com 已正常访问,Go 示例远程运行成功二、先让 go-dev 在 EdgeOne 上独立运行
我没有一开始就关闭旧域名,而是先让新旧两个域名并存。
首先在腾讯云 EdgeOne 中新增:
go-dev.shuijingwanwq.com
源站继续使用现有服务器:
121.40.248.29
回源协议:
HTTPS
端口:
443
为了尽快验证新域名,最开始我暂时把 EdgeOne 的回源 Host 设置成:
go-tour.shuijingwanwq.com
也就是说,此时链路实际上是:
go-dev → EdgeOne → go-tour 源站虚拟主机 → Go Tour
这样可以直接复用前一天已经验证正常的 Nginx HTTPS 配置,不需要先改服务器,就能先确认新公网域名是否正常工作。
Cloudflare DNS 中则增加新的 CNAME:
go-dev.shuijingwanwq.com
指向:
go-dev.shuijingwanwq.com.eo.dnse2.com
代理状态保持为“仅 DNS”,由 EdgeOne 正式承担 CDN 与 HTTPS 服务。
随后在 EdgeOne 中为新域名申请免费 HTTPS 证书,并使用自动验证。
证书部署完成后,新域名很快就已经能够正常访问:
https://go-dev.shuijingwanwq.com/tour/welcome/1
中文课程正文、右侧编辑器和页面静态资源均正常显示。
我又实际点击了一次“运行”,确认生产环境仍然能够通过远程 Go Playground 执行示例代码,最终正常返回:
Hello, 世界
程序已退出。
期间曾出现过一次偶发超时,不过再次运行后立即成功。
因此,这个问题没有被当作域名迁移的阻塞项。后续如果远程运行超时变成高频问题,再单独排查即可。
三、旧域名不直接删除,而是永久 301 到 go-dev
虽然原来的 go-tour.shuijingwanwq.com 才刚刚正式上线不久,但我仍然没有选择直接废弃它。
原因很简单。
即使只上线了一天,也可能已经存在:
- 浏览器历史记录;
- 博客文章中的旧链接;
- 搜索引擎已经发现但尚未正式收录的 URL;
- 我自己保存或分享过的地址。
因此,更稳妥的方案仍然是:
旧域名永久 301 到新域名,并保持原路径不变。
例如:
https://go-tour.shuijingwanwq.com/tour/welcome/1
永久跳转到:
https://go-dev.shuijingwanwq.com/tour/welcome/1
而不是所有旧页面都统一跳到新站首页。
我直接在 EdgeOne 的规则引擎中创建了一条规则。
匹配条件:
HOST = go-tour.shuijingwanwq.com
操作:
- 访问 URL 重定向;
- 目标协议:HTTPS;
- 目标 Hostname:
go-dev.shuijingwanwq.com; - 目标路径:跟随请求;
- 查询参数:保留;
- 状态码:301。

go-tour 域名永久 301 到 go-dev,同时保持原路径与查询参数发布后立即进行了实际验证:
HTTP/2 301
location: https://go-dev.shuijingwanwq.com/tour/welcome/1
server: TencentEdgeOne
这说明重定向直接发生在 EdgeOne 边缘节点,不需要请求再进入源站 Nginx。
这一点也为后面彻底删除服务器上的旧 go-tour 虚拟主机创造了条件。
四、重新创建 go-dev 的独立 Nginx 虚拟主机
虽然新域名已经可以正常访问,但此时它仍然临时通过:
Host: go-tour.shuijingwanwq.com
回源到旧 Nginx 虚拟主机。
如果想真正完成迁移,服务器最终仍然应该只保留:
go-dev.shuijingwanwq.com
因此下一步是在源站创建新的独立 Nginx 虚拟主机。
我的服务器使用 OneinStack,因此这里继续遵循一个原则:
凡是 OneinStack 已经提供脚本或内置流程的操作,优先使用 OneinStack,而不是直接绕过它调用底层工具。
这次使用的是:
cd /root/oneinstack
./vhost.sh --proxy --dnsapi
其中:
--proxy:创建反向代理虚拟主机;--dnsapi:使用 DNS API 方式签发 HTTPS 证书。
输入的新域名是:
go-dev.shuijingwanwq.com
HTTPS 使用 Let’s Encrypt。
证书密钥类型继续采用:
ec-256
DNS provider 使用:
cf
也就是 Cloudflare DNS 验证。
反向代理目标继续保持:
http://127.0.0.1:3999
这样新虚拟主机最终仍然连接现有的:
go-tour.service
并没有重新部署 Go 应用。
五、操作过程中发现旧 OneinStack 目录仍然存在
这次操作过程中,还发现了一个容易在以后继续造成混乱的问题。
服务器上同时存在:
/root/oneinstack
以及之前升级 PHP 8.5 时使用的新版:
/root/oneinstack-latest-20260804
而原来的 /root/oneinstack 实际上还是 2023 年左右留下来的旧版本。
也就是说,如果继续习惯性地:
cd /root/oneinstack
很容易再次调用到旧脚本。
既然新版 OneinStack 已经经过 PHP 8.5 等实际维护验证,我决定趁这次一起把目录规范掉。
先确认没有 cron、systemd 或 Shell 配置硬编码引用旧路径后,将旧版本暂时改名:
/root/oneinstack-old-20260812
再把:
/root/oneinstack-latest-20260804
提升为标准目录:
/root/oneinstack
完成全部生产验收后,旧的 2023 年版本最终被删除。
这样以后所有 OneinStack 运维统一从:
/root/oneinstack
进入,避免再次因为新旧脚本并存而选错版本。
六、OneinStack 自动生成的 location 再次带来静态资源 404 隐患
新的 go-dev 反向代理虚拟主机创建完成后,还有一个前一天上线时已经遇到过的问题需要再次处理。
OneinStack 自动生成的 Nginx 配置中包含额外的 location 规则。
对于普通网站来说,这些规则可能没有问题。
但 A Tour of Go 的静态资源本身是由 Go Tour 服务提供的,例如:
/tour/static/css/app.css
如果被 Nginx 的本地静态文件规则提前截获,就不会继续进入:
proxy_pass http://127.0.0.1:3999
最终就可能返回:
404
因此,新虚拟主机生成后,我再次删除了那两段会截获 Tour 静态资源的额外 location。
修改后执行:
nginx -t && service nginx reload
这里还确认了一个当前服务器环境的实际情况:
service nginx configtest
在这台服务器上不可用。
它会提示:
service 命令只支持基础 LSB 动作
因此目前已经验证可用的方式是:
nginx -t
检查配置,通过后再:
service nginx reload
或者直接组合:
nginx -t && service nginx reload
随后再次验证静态资源:
https://go-dev.shuijingwanwq.com/tour/static/css/app.css
返回:
200
https://go-dev.shuijingwanwq.com/tour/static/lib/codemirror/lib/codemirror.css
同样返回:
200
这说明 /tour/static/ 已经重新通过主反向代理交给 Go Tour 服务处理。
七、EdgeOne 正式切换到新的 go-dev 源站
新的 go-dev Nginx 虚拟主机、HTTPS 和静态资源全部验证正常后,就可以结束最初的临时过渡配置。
EdgeOne 中新域名原来的回源 Host 是:
go-tour.shuijingwanwq.com
此时正式调整为:
go-dev.shuijingwanwq.com
最终生产链路变成:
go-dev.shuijingwanwq.com
→ EdgeOne
→ HTTPS 回源 121.40.248.29:443
→ Host: go-dev.shuijingwanwq.com
→ 新 Nginx 虚拟主机
→ 127.0.0.1:3999
→ go-tour.service
这一步完成以后,新生产域名已经彻底不再依赖旧 go-tour Nginx 虚拟主机。
八、彻底删除服务器上的旧 go-tour
既然:
- 新
go-dev页面正常; - HTTPS 正常;
- 静态资源正常;
- Go 示例运行成功;
- EdgeOne 已正式回源到
go-dev; - 旧域名 301 在 EdgeOne 边缘完成;
那么服务器就没有必要继续保留旧的:
go-tour.shuijingwanwq.com
Nginx 虚拟主机。
这次继续使用 OneinStack 自带的删除流程:
cd /root/oneinstack
./vhost.sh --del
只删除:
go-tour.shuijingwanwq.com
而新的:
go-dev.shuijingwanwq.com
保持不动。
随后继续清理了:
- 旧 Nginx 虚拟主机;
- 旧 Nginx 备份配置;
- 旧 SSL 文件;
- 旧 acme.sh 证书管理记录;
/root/.acme.sh/go-tour.shuijingwanwq.com_ecc残留目录。
最终,和这个项目相关的 acme.sh 记录只剩:
go-dev.shuijingwanwq.com
证书仍然是:
ec-256
CA:
LetsEncrypt.org

go-dev 生产入口九、旧 go-tour 为什么还不能从 Cloudflare 和 EdgeOne 删除
服务器里的旧 go-tour 已经彻底删除,但 Cloudflare 和 EdgeOne 中的旧域名却不能一起删除。
这是因为它现在承担着一个新的职责:
旧 URL 的 HTTPS 兼容入口和永久 301。
因此 Cloudflare 中仍然需要保留:
go-tour.shuijingwanwq.com
指向 EdgeOne 的 CNAME。
EdgeOne 中也继续保留:
go-tour.shuijingwanwq.com
以及对应的 HTTPS 证书。
否则用户访问:
https://go-tour.shuijingwanwq.com/…
时,可能在获得 301 之前就因为 HTTPS 失败而无法继续访问。
因此最终架构并不是“彻底删除旧域名”,而是:
go-dev.shuijingwanwq.com
→ 正式生产站
→ EdgeOne
→ go-dev Nginx
→ Go Tour
go-tour.shuijingwanwq.com
→ EdgeOne
→ HTTPS
→ 301 到 go-dev 同路径
另外,我还把旧 go-tour 在 EdgeOne 中的备用回源 Host 调整成了:
go-dev.shuijingwanwq.com
因此,即使在极端情况下没有命中 301 而发生回源,也不会再依赖已经删除的旧 Nginx 虚拟主机。
十、最终验收:200、200、301
整个迁移完成后,我整理了一组非常简单的最终验收。
新正式页面:
https://go-dev.shuijingwanwq.com/tour/welcome/1
返回:
200
新域名静态资源:
https://go-dev.shuijingwanwq.com/tour/static/css/app.css
返回:
200
旧域名:
https://go-tour.shuijingwanwq.com/tour/welcome/1
返回:
301
并且:
Location: https://go-dev.shuijingwanwq.com/tour/welcome/1

至此,这次生产域名迁移可以正式判定完成。
十一、顺便增加一份生产运维手册
这次迁移过程中还有一个比较明显的体会。
项目持续时间越来越长以后,只依赖历史聊天记录来记住:
- OneinStack 当前使用哪个目录;
- Nginx 应该怎么重载;
- HTTPS 证书采用什么方式;
- EdgeOne 如何回源;
- 哪些内部名称不能因为公网域名变化而重命名;
并不够可靠。
因此,在完成域名迁移后,我又给项目新增了一份:
docs/PRODUCTION_RUNBOOK.md
专门记录当前生产环境的实际维护方式。
它和:
docs/PROJECT_STATE.md
承担不同职责。
PROJECT_STATE.md 主要记录:
项目现在做到哪里,以及为什么变成现在这样。
而 PRODUCTION_RUNBOOK.md 主要记录:
当前生产环境应该怎么维护。
其中固定记录了:
- Cloudflare 与 EdgeOne 架构;
go-dev与go-tour的职责;go-tour.service与/data/go-tour/;- Nginx 虚拟主机和证书路径;
/root/oneinstack作为唯一正式维护目录;- OneinStack 优先原则;
nginx -t && service nginx reload;/tour/static/不能被本地location截获;- 最小生产验收命令;
- 本地代理可能干扰
curl --resolve源站直连判断等注意事项。
以后再处理生产服务器问题时,可以优先查看这份文档,而不需要重新从大量历史操作中寻找细节。
十二、为什么刚上线一天,我仍然选择立即换域名
如果这是一个已经运行几年、积累了大量外链和搜索流量的网站,我不会这么轻易更换生产域名。
但这次情况完全不同。
go-tour.shuijingwanwq.com 才刚刚正式上线。
此时迁移的成本非常低,而继续使用它,则意味着以后长期接受:
go-tour.../tour/...
这样的重复结构。
而前一天已经确认,直接删除 /tour/ 又会牵涉过多已经稳定下来的程序和验证逻辑。
所以与其为了 URL 美化重新改动 Tour 本身,不如只调整站点入口。
更重要的是,项目未来未必永远只停留在 A Tour of Go。
如果后续 Tour 能够获得一定流量,我还可能选择性翻译 go.dev 中其他值得长期维护的内容。
这时:
go-dev.shuijingwanwq.com/tour/
可以继续承载 A Tour of Go。
未来如果确实有必要,也可以自然扩展到类似:
go-dev.shuijingwanwq.com/doc/
或其他内容路径。
因此,这次更换域名并不只是一次 URL 美化。
它实际上是在项目完成第一阶段正式上线之后,对未来内容边界做的一次小幅调整:
保留已经稳定的
/tour/,把更宽泛的语义放到站点级域名上。
十三、当前最终状态
截至这次迁移完成,A Tour of Go 多语言翻译项目当前生产状态为:
正式域名:
https://go-dev.shuijingwanwq.com
A Tour of Go:
https://go-dev.shuijingwanwq.com/tour
旧域名:
https://go-tour.shuijingwanwq.com
职责:
仅保留 HTTPS 和同路径永久 301。
生产服务:
go-tour.service
监听:
127.0.0.1:3999
生产目录:
/data/go-tour/
Nginx 正式虚拟主机:
go-dev.shuijingwanwq.com
HTTPS:
Let’s Encrypt + ec-256
DNS:
Cloudflare
CDN:
腾讯云 EdgeOne
OneinStack 正式维护目录:
/root/oneinstack
最终 HTTP 验收:
- 新课程页面:200;
- 新静态资源:200;
- 旧域名:301;
- 原路径:完整保留;
- 中文页面:正常;
- Go 示例远程运行:正常。
从这一刻开始,go-dev.shuijingwanwq.com 就成为这个项目新的正式生产入口。
而 go-tour.shuijingwanwq.com 则正式退回到兼容重定向入口的角色。
接下来,我还准备给整个站点增加更明确的“非官方社区多语言翻译项目”说明,并进一步整理项目介绍和品牌边界。不过这些内容不再混入本次域名迁移,而会作为下一阶段单独处理。
需要长期技术维护或远程问题排查?
我是拥有 15+ 年经验的 PHP / Go 后端工程师,长期关注已有系统维护、Bug 修复、性能优化、服务器排查、WordPress 网站维护和小功能迭代。
如果你的项目遇到以下情况,可以先从一次小问题排查开始合作:
- ✅ PHP / Laravel / Yii2 老项目无人维护
- ✅ Go / Gin 后端接口需要排查或优化
- ✅ WordPress 网站访问慢、报错或插件冲突
- ✅ Nginx / MySQL / Redis / Linux 服务器异常
- ✅ CDN / Cloudflare / DNS / HTTPS 配置问题
- ✅ 需要长期远程技术支持或兼职维护
更多介绍请查看:关于我 & 合作
微信:13980074657
邮箱:shuijingwanwq@gmail.com
Telegram:@shuijingwan
GitHub:https://github.com/shuijingwan


发表回复