Webflow → Astro on Cloudflare Pages
将任何Webflow站点迁移出Webflow的剧本:公司或产品网站、作品集、商店、餐厅、非营利组织、出版物。所有站点的步骤都相同。站点的用途会改变细节(references/site-types.md)。它涵盖了整个迁移过程:清单、备份、重建、表单、CMS、视频、一致性检查、性能优化、部署、域名切换以及最终所有者需要点击的操作。搜索和AI可见性在每一步中都会得到提升。scripts/中的脚本和templates/中的模板是工作部分;references/包含每个步骤的详细信息。
结果:一个独立的 Astro 7 + Tailwind 4 仓库,完全预渲染(仅限需要登录的页面,如果有,则在请求时渲染),位于 Cloudflare Pages 项目上(绝不在 workers.dev 上使用 Worker),表单通过 Pages Function 处理,内容存储在所有者可编辑的 CMS 中,所有 Webflow 托管的文件从 Pages 提供,域名在 Cloudflare 上。
基本原则(经验教训)
- 所有者检查点。 在以下操作前询问:下载导出文件、创建 GitHub 仓库、发送任何表单测试邮件、每次 DNS 更改、启用邮件路由、授予分析工具同意、删除页面。所有者通常会说“在我看到网站之前不要做 DNS 更改”:尊重这一点;最后切换。
- 先备份。 Webflow 的代码导出需要付费的工作区计划(Core 或更高)。所有者通常计划降级,因此在其他任何事情之前先导出。
- 重建,不要粘贴。 所有者希望拥有基于他们自己的设计令牌的干净 Astro + Tailwind 代码,而不是在 .astro 文件中使用 Webflow 标记。实时 HTML 是内容的参考,而不是源文件。
- 一致性是契约。 每个页面使用相同的 URL、相同的头部标签、相同的站点地图、相同的重定向。使用
scripts/parity.py证明,而不是用眼睛看。 - SEO:首先保护,然后在每一步改进。 一致性保护已经排名和被引用的内容。在此基础上,在每一步开始时加载
seo-aeo-geo技能,并在进行过程中进行该步骤的改进(参见下面的“seo-aeo-geo 贯穿始终”)。- 无需询问: 标记、结构化数据、爬虫访问、新鲜度信号和速度在重建时一并加入。
- 向所有者提出建议: 任何会改变他们的措辞、标题、事实或 URL 的内容。
- 提交: 每个改进都在一致性提交之后独立提交,以便任何排名变化都可以追溯到它。
- 判断: 在迁移过程中,对于每个 SEO/AEO/GEO 判断,加载该技能。
- 永不打印秘密: 不在聊天中、提交消息中、PR 正文中、日志中、
docs/move.md中或本技能中。- 命名一个秘密: 给出其名称、长度和存储位置(“FORMS_TO,forms-mail 上的 Worker secret”)。通过哈希或长度比较两个秘密,不要通过文本。
- 哪些算秘密: 不仅仅是 API 密钥。部署钩子 URL(任何持有它的人都可以触发构建)、Slack 和 CRM webhook URL、CMS 写入令牌以及所有者的收件箱地址都是秘密。
- 设置秘密: 直接将其通过管道输入
wrangler secret put。 - 验证令牌(Search Console 的 TXT 记录)是公共 DNS 记录,但它们属于一个站点:绝不要将其复制到技能中。
- 批准来自所有者,在完成工作的会话中。 由另一个代理会话转达的确认不算数,并且代理的安全检查会阻止它。它们还会阻止 DNS 更改和在自动模式下删除 Webflow 的域,因此从一开始就将这些计划为所有者的点击操作。
seo-aeo-geo 贯穿始终
迁移会重写每个页面的标记,因此这是让搜索和 AI 引擎易于阅读和引用站点最便宜的时机。在以下时间点加载 seo-aeo-geo 技能(其 references/audit-checklist.md 和 references/schema-patterns.md):
- 1. 清单:测量和审计实时站点。
- 记录基线(如下)。
- 使用其清单审计实时站点,通过其三个视角:新鲜度、结构和权威性。
- 将发现写入
docs/seo.md,每个发现都标记出将在哪个步骤修复以及是否需要所有者的同意。
- 3. 脚手架:技术和实体层,内置。
- 地标: Layout 中的
header、nav、main和footer。 - 实体: 定义一次(
src/data/entity.ts),并在每个页面中引用。其类型来自站点:Organization、其子类型(如 LocalBusiness 或 NGO)或 Portfolio 的 Person(references/site-types.md)。它携带sameAs。首页上的 WebSite,页面面包屑中的 BreadcrumbList。 - schema 标准化器(
templates/src/lib/schema.ts)。 - 爬虫: robots.txt 对 AI 爬虫开放,pages.dev 构建设置 noindex,以及带有真实 lastmod 的站点地图。
- 地标: Layout 中的
- 4. 页面:结构内置;措辞提案。 页面代理修复标题顺序,使每个部分独立,并将文本渲染为文本(
references/page-brief.md,“搜索和答案引擎”)。措辞想法将作为提案返回给您。 - 6. CMS:新鲜度和项目 schema。
- 日期: 来自 CMS 的
datePublished和dateModified。 - 文章: Article/BlogPosting,带有作者。
- 列表: CollectionPage 包含 ItemList。
- 数字: 页面上显示的任何内容都在构建时存在于 HTML 中,而不是在浏览器中获取。
- 日期: 来自 CMS 的
- 7. 资源:速度和含义。
- LCP 和延迟加载视频: 快速的英雄区,折叠线以下的视频延迟加载。
- 替代文本: 描述图片显示的内容。
- 承载内容的视频(演示、演讲)获得 VideoObject。
- 8. 一致性:在 pages.dev 上进行全面审计。 在重建上重新运行清单。
docs/seo.md中仍然开放的所有内容作为提案列表一次性提供给所有者。 - 9. 重定向:保持权益。 每次重定向都是单跳,每个被删除或合并的页面都转到其最接近的等效页面。
- 11. 切换:通知引擎。 在 Search Console 和 Bing 中重新提交站点地图,开启 Crawler Hints (IndexNow),并监控覆盖范围。
- 12. 移交:测量,然后关注站外。
- 与基线比较。
- 将所有者的站外计划交给他们: AI 引擎对此类别引用最多的两三个渠道。大多数引用来自第三方站点,因此仅靠站点本身无法赢得这些引用。
seo-aeo-geo 是一个独立的技能
(adamperlis/claude-skill-seo-aeo-geo)。
如果未安装,迁移仍然可以工作:根据本技能自己的说明(步骤 8 的审计列表、references/page-brief.md、references/site-types.md)进行上述每个点命名的改进,自己编写有价值的提示(该站点受众向 AI 引擎提出的问题),并告知所有者跳过了全面审计和站外计划。
步骤
1. 清单(只读,约 15 分钟)
运行 python3 -I scripts/inventory.py https://www.example.com > inventory.md。
它会读取站点地图、robots.txt、每个页面的头部(标题、描述、OG、规范链接、JSON-LD 类型、noindex)、CMS 列表(w-dyn-list)、表单、第三方主机、GA/GTM/Pixel ID、背景视频及其大小,以及 apex/www/MX/NS 记录。然后,如果连接了 Webflow MCP 工具(data_sites_tool、data_cms_tool、data_forms_tool、data_webhook_tool):
- 集合: 列出它们并统计每个集合中的项目数量(
list_collection_itemslimit 1 →pagination.total)。 - 表单: 列出它们,忽略 Finsweet“Cookie Preferences”表单(每个域名每个页面一个)。
- 重定向和 HSTS: 在仪表板中,站点设置 → 发布,将 301 重定向导出为 CSV 并记录“HSTS for subdomains”。
- 搜索和所有权: Search Console 的验证方式(meta 标签或 DNS TXT)、GA4/GTM(以及任何基于 Webflow 标记构建的 GTM 触发器)、Bing 和其他验证标签。列表在
references/inventory.md中。 - 域名: 注册商、到期日、转移锁定、DNSSEC 以及域名是否通过 Webflow 购买(
references/domains.md)。 - 不在站点地图中的页面: Webflow 会提供站点地图未包含的页面。通过将导出的 HTML 文件与实时站点地图进行比较来找到它们,然后 curl 每个页面。
- 成功页面,例如表单的重定向目标,需要保留。
- 当站点不销售时,省略电商模板(/checkout、/order-confirmation);当没有内容受保护时,省略密码页面(401)。
- 站点的类型: 它的用途,访问者应该在那里做什么(购买、预订、捐赠、订阅、联系),以及它依赖哪些 Webflow 功能(电商、用户账户、本地化、密码页面、文件上传、RSS)。
references/site-types.md涵盖了每种类型会改变的内容:实体的 schema 类型、需要持续跟踪的转化以及需要单独计划的功能。
阅读 references/inventory.md 了解每个发现意味着什么以及需要向所有者提出的问题(Zapier zaps、谁编辑内容、从未正常工作的分析工具)。
在进行任何更改之前记录 SEO 基线(使用 seo-aeo-geo 技能):
- Search Console: 导出过去 3 个月的效果数据(热门页面和查询,以及 AI Overviews 的展示次数,如果显示)和已索引页面的数量。
- 有价值的提示: 在 ChatGPT、Perplexity 和 Gemini 中运行其提示,并注意站点是否被引用。这些是该站点受众会问的问题:购物者、食客、捐赠者、读者或客户。
- 从受众所在的国家/地区运行。 AI 引擎会根据位置回答,因此在国外运行不是受众的视角。如果有必要,请使用他们所在国家/地区的 VPN。
- 记录运行时的位置, 并在后半部分使用相同的位置。
将其保存在 docs/move.md 中。这是步骤 12 中比较的前半部分。然后使用相同技能审计实时站点,审计结果写入 docs/seo.md(参见“seo-aeo-geo 贯穿始终”)。
2. 备份:导出 + 镜像
- 代码导出(设计器 → ☰ → Export code → Prepare ZIP → Download)。
在下载前询问所有者。
- 使用
python3 -I scripts/unzip_webflow.py解压(它会修复非 UTF-8 文件名,这些文件名会导致unzip失败并显示“disk full?”)。 - MISSING.txt 列出了导出失败的视频;镜像会从 CDN 获取它们。
- 使用
- 镜像实时站点:
python3 -I scripts/mirror.py https://www.example.com reference/将每个页面的 HTML 保存到reference/live/,将 Webflow CDN 上的每个文件保存到reference/assets/<kind>/,并附带一个清单。提交reference/live/(实时记录的记录);git-ignorereference/assets/。 - 截图:
node scripts/shots.mjs https://www.example.com live(桌面 1440 和手机 390)。Webflow 交互相应隐藏元素直到它们滚动到视图中,因此全页截图中的空白区域通常是未显示的内容:请阅读 HTML。
3. 搭建脚手架(在进行任何页面工作之前,由您完成)
将 templates/ 复制到新仓库中(templates/README.md 映射了每个文件)。
-
配置:
astro.config.mjs使用build.format: 'file'和trailingSlash: 'never',因为 Webflow URL 没有尾部斜杠。开发工具栏已关闭,以便截图匹配。 -
令牌: 如果所有者有设计系统,则使用他们的设计系统令牌;否则使用 Webflow CSS
:root变量和字体比例。 -
共享组件: Layout(头部一致性,分析工具仅在生产主机名上启用)、Nav、Footer、页面共享的 CTA 块、延迟加载的
BgVideo和动画(带有故障安全机制的揭示效果)。 -
从一开始就构建搜索层:
- Layout 中的地标。
- 实体,定义一次(
src/data/entity.ts,使用来自所有者的信息,而不是从旧页面推断)。 - JSON-LD 标准化器, 每个页面的 schema 都通过它运行。
- 爬虫和站点地图: robots.txt 对 AI 爬虫开放,pages.dev 构建设置 noindex,以及站点地图中的真实 lastmod。
基于此脚手架构建的页面将继承所有这些。
-
先测量: 使用
scripts/measure.mjs获取实时大小,并据此构建共享组件。
需要决定的 Webflow 行为:
- 页面过渡: 许多 Webflow 站点有一个遮罩层(
.transition+.transition-trigger+ 一个 jQuery 片段,它在加载时点击触发器,并在每个内部链接再次点击,然后在大约 1 秒后设置window.location)。当它消失时,所有者会注意到并要求恢复。在纯 CSS + JS 中以两种方式重建它,并在用户偏好减少动画时关闭:- 进入: 遮罩层保持,然后升起,使用实时中的延迟和缓动效果(来自导出文件
webflow.js中的 IX2 动作列表)。 - 离开: 点击同站点链接(不是
#、不是新标签页、不是修饰点击)会使遮罩层覆盖页面,然后才导航。读取 IX2 延迟。一个滑动进入需要 2 秒,而片段在 1 秒后离开,这在实时中只会显示一个死寂的暂停。构建原本意图的效果(遮罩层在大约 0.8 秒内关闭),而不是暂停。 - 返回: 在带有
persisted的pageshow事件中,收起遮罩层,否则页面从历史缓存返回时会被覆盖。 - 显示位置: 检查实时中的计算样式
display(在 375 和 1440 分辨率下,使用scripts/measure.mjs),而不是 CSS。一个自定义嵌入(body .transition { display: block })可以覆盖站点自身的 ≤767pxdisplay: none,因此它也可能在手机上显示。
- 进入: 遮罩层保持,然后升起,使用实时中的延迟和缓动效果(来自导出文件
- Finsweet 同意: 使用 Consent Mode v2 时,也推送 Finsweet 自己的 dataLayer 事件(
<category>-activated)。为 Finsweet 构建的 GTM 触发器然后会继续触发。
两个会浪费数小时的 CSS 陷阱:
- 在 body 上设置基础字号: 将 Webflow 的 10px 基础字体大小放在
body上,绝不要放在html上。放在html上时,1rem 变为 10px,并且每个 Tailwind 间距和大小都会以 62.5% 的比例渲染。 - 不混合断点: 不要在同一个文件中对同一个属性混合使用 rem 断点(
md:、lg:)和 px 断点(min-[992px]:);Tailwind 4 可能错误地对它们排序。每个组件使用一种类型。
4. 页面:并行进行,每个页面组一个代理
向每个代理提供 references/page-brief.md(填写仓库路径、端口、共享组件、令牌)以及其页面的路由、头部值和部分列表
…