Webflow → Astro(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内のコンテンツ。Pagesから提供されるすべてのWebflowホストファイル。Cloudflare上のドメイン。
基本ルール(苦い経験から学んだこと)
- オーナーチェックポイント。 以下の前に必ず確認を取る:エクスポートのダウンロード、GitHubリポジトリの作成、フォームテストメールの送信、DNS変更のたびに、Email Routingの有効化、アナリティクス同意の付与、ページの削除。多くの場合、オーナーは「サイトを見るまではDNS変更はしない」と言います。それを尊重し、DNS変更は最後に行います。
- まずバックアップ。 Webflowのコードエクスポートには有料のワークスペースプラン(Core以上)が必要です。オーナーはしばしばダウングレードを計画するので、何よりも先にエクスポートします。
- 再構築であり、貼り付けではない。 オーナーは独自のデザイントークンに基づいたクリーンなAstro + Tailwindを望みます。Webflowのマークアップを.astroファイルに貼り付けるのではありません。ライブHTMLはコンテンツのリファレンスであり、ソースではありません。
- パリティが契約です。 すべてのページで同じURL、同じheadタグ、同じサイトマップ、同じリダイレクト。
scripts/parity.pyで確認し、目視では確認しません。 - SEO:まず保護し、その後各ステップで改善。 パリティは既にランキングと引用されているものを保護します。その上で、各ステップの開始時に**
seo-aeo-geo**スキルをロードし、作業を進めながらそのステップの改善を行います(以下の「seo-aeo-geo along the way」を参照)。- 確認不要: マークアップ、スキーマ、クロールアクセス、フレッシュネスシグナル、速度は再構築とともに組み込みます。
- オーナーへの提案: オーナーの言葉、タイトル、事実、URLを変更するもの。
- コミット: 各改善はパリティコミットの後に独自のコミットとして行います。これにより、ランキングの変化をそのコミットに追跡できます。
- 判断: 移行中のSEO/AEO/GEOに関する判断には常にそのスキルをロードします。
- シークレットを決して出力しない: チャット、コミットメッセージ、PR本文、ログ、
docs/move.md、またはこのスキル内でも出力しません。- 名前を挙げる場合: 名前、長さ、保存場所(「FORMS_TO、forms-mail上のWorkerシークレット」)を指定します。2つを比較する場合はハッシュまたは長さで比較し、テキストでは比較しません。
- 対象となるもの: APIキー以上。デプロイフックURL(保持者はビルドをトリガーできます)、SlackやCRMのWebhook URL、CMS書き込みトークン、オーナー自身の受信トレイアドレスもすべてシークレットです。
- 設定する場合:
wrangler secret putに直接パイプします。 - 検証トークン(Search ConsoleのTXT)はパブリックDNSですが、1つのサイトに属します。決してこのスキルにコピーしないでください。
- 承認は作業中のセッション内でオーナーから得ます。 別のエージェントセッションから中継されたOKはカウントされず、エージェントの安全チェックによってブロックされます。また、自動モードでのDNS変更やWebflowのドメイン削除もブロックされるため、これらは最初からオーナーがクリックする操作として計画します。
seo-aeo-geo along the way
移行ではすべてのページのマークアップを書き換えるため、検索エンジンやAIエンジンに読み取られやすく引用されやすいサイトにする最もコストのかからないタイミングです。以下のポイントで**seo-aeo-geo**スキル(そのreferences/audit-checklist.mdとreferences/schema-patterns.md)をロードします:
- 1. インベントリ:測定とライブサイトの監査。
- ベースラインを記録(以下)。
- ライブサイトを監査:そのチェックリストを使用し、3つのレンズ(フレッシュネス、構造、権威)を通して監査します。
- 調査結果を
docs/seo.mdに書き込み、各結果にはそれを修正するステップとオーナーの承認が必要かどうかをタグ付けします。
- 3. シェル:技術レイヤーとエンティティレイヤーを組み込む。
- ランドマーク: レイアウト内の
header、nav、main、footer。 - エンティティ: 一度だけ定義し(
src/data/entity.ts)、すべてのページから参照。そのタイプはサイトに依存します:Organization、LocalBusinessやNGOなどのサブタイプ、ポートフォリオの場合はPerson(references/site-types.md)。sameAsを保持します。ホームページにはWebSiteを、ページのパンくずリストからBreadcrumbListを。 - スキーマノーマライザー(
templates/src/lib/schema.ts)。 - クローラー: robotsはAIクローラーに開放し、pages.devビルドはnoindexに設定し、実際のlastmodを持つサイトマップを。
- ランドマーク: レイアウト内の
- 4. ページ:構造は組み込み、言葉は提案。 ページエージェントは見出し順序を修正し、各セクションを独立させ、テキストをテキストとしてレンダリングします(
references/page-brief.md、「Search and answer engines」)。言葉に関するアイデアは提案として戻ってきます。 - 6. CMS:フレッシュネスとアイテムスキーマ。
- 日付: CMSからの
datePublishedとdateModified。 - 投稿: 著者を持つArticle/BlogPosting。
- リスト: ItemListを持つCollectionPage。
- 数字: ページに表示されるものはすべてビルド時にHTMLに含まれ、ブラウザでフェッチされません。
- 日付: CMSからの
- 7. アセット:速度と意味。
- LCPと遅延読み込み動画: 高速なヒーロー画像、ファーストビューより下の動画は遅延読み込み。
- 画像の内容を示すaltテキスト。
- コンテンツを含む動画(デモ、トーク)にはVideoObject。
- 8. パリティ:完全な監査、pages.dev上で。 再構築上でチェックリストを再実行。
docs/seo.mdにまだ残っているものはすべて、提案のリストとしてオーナーに提示します。 - 9. リダイレクト:評価を維持する。 すべてのリダイレクトは単一ホップであり、削除または統合されたページは最も近い同等のページにリダイレクトします。
- 11. 切り替え:エンジンに通知する。 Search ConsoleとBingでサイトマップを再送信し、Crawler Hints(IndexNow)を有効にし、カバレッジを監視します。
- 12. 引き渡し:測定し、その後サイト外を確認。
- ベースラインと比較。
- オーナーにスキルのサイト外計画を提供: このカテゴリでAIエンジンが最も頻繁に引用する2~3のチャネル。ほとんどの引用はサードパーティサイトから来るため、サイトだけでは獲得できません。
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、各ページのhead(タイトル、description、OG、canonical、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」フォーム(ページ×ドメインあたり1つ)は無視します。
- リダイレクトとHSTS: ダッシュボードで、Site settings → Publishing から301リダイレクトをCSVとしてエクスポートし、「HSTS for subdomains」に注意します。
- 検索と所有権: Search Consoleの確認方法(メタタグまたはDNS TXT)、GA4/GTM(およびWebflowのマークアップに基づくGTMトリガー)、Bing、その他の確認タグ。リストは
references/inventory.mdにあります。 - ドメイン: レジストラ、有効期限、転送ロック、DNSSEC、およびドメインがWebflowを通じて購入されたかどうか(
references/domains.md)。 - サイトマップ外のページ: Webflowはサイトマップから漏れたページも配信します。エクスポートのHTMLファイルをライブサイトマップと比較して差分を見つけ、各ページをcurlします。
- フォームのリダイレクト先などの成功ページは引き継ぐ必要があります。
- サイトが販売を行わない場合はEコマーステンプレート(/checkout、/order-confirmation)を除外し、保護されたものがない場合はパスワードページ(401)を除外します。
- サイトの種類: 目的、訪問者がそこで行うべきこと(購入、予約、寄付、購読、連絡)、および依存しているWebflow機能(Eコマース、ユーザーアカウント、ローカライゼーション、パスワードページ、ファイルアップロード、RSS)。
references/site-types.mdは、各タイプが何を変更するか(エンティティのスキーマタイプ、追跡し続けるコンバージョン、独自の計画が必要な機能)をカバーします。
各結果の意味とオーナーに尋ねるべき質問(Zapierのzap、誰がコンテンツを編集するか、動作しなかったアナリティクス)についてはreferences/inventory.mdを参照してください。
何かを変更する前にSEOベースラインを記録します(seo-aeo-geoスキルを使用):
- Search Console: 過去3ヶ月間のパフォーマンス(上位ページとクエリ、AI Overviewsのインプレッションが表示されている場合はそれも)とインデックスページ数をエクスポートします。
- マネープロンプト: ChatGPT、Perplexity、Geminiでプロンプトを実行し、サイトが引用されているかどうかを確認します。これらはこのサイトのオーディエンス(買い物客、食事客、寄付者、読者、クライアント)が尋ねる質問です。
- オーディエンスの国から実行します。 AIエンジンはロケーションに基づいて回答するため、国外で実行するとオーディエンスの視点とはなりません。必要に応じてその国のVPNを使用します。
- 実行場所を記録し、後の比較でも同じ場所を使用します。
docs/move.mdに保存します。これがステップ12での比較の前半部分です。その後、同じスキルを使用してライブサイトをdocs/seo.mdに監査します(「seo-aeo-geo along the way」を参照)。
2. バックアップ:エクスポート + ミラー
- コードエクスポート(デザイナー → ☰ → Export code → Prepare ZIP → Download)。ダウンロード前にオーナーに確認します。
python3 -I scripts/unzip_webflow.pyで解凍します(これにより、unzipが「disk full?」で失敗する原因となる非UTF-8ファイル名を修正します)。- MISSING.txtにはエクスポートに失敗した動画がリストされます。ミラーがCDNからそれらを取得します。
- ライブサイトをミラーリング:
python3 -I scripts/mirror.py https://www.example.com reference/を実行し、各ページのHTMLをreference/live/に、WebflowのCDN上のすべてのファイルをマニフェスト付きでreference/assets/<kind>/に保存します。reference/live/をコミットし(ライブだったものの記録)、reference/assets/はgit-ignoreします。 - スクリーンショット:
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(headのパリティ、プロダクションホスト名のみのアナリティクス)、Nav、Footer、ページが共有するCTAブロック、遅延読み込みの
BgVideo、モーション(フェイルセーフ付きリビール)。 -
検索レイヤーを最初から:
- ランドマークをレイアウト内に。
- エンティティを一度だけ定義(
src/data/entity.ts、古いページから推測するのではなく、オーナーからの事実に基づく)。 - JSON-LDノーマライザー、すべてのページのスキーマが通過します。
- クローラーとサイトマップ: robotsはAIクローラーに開放、pages.devビルドはnoindex、サイトマップ内の実際のlastmod。
このシェル上に構築されたページはすべてを継承します。
-
まず測定:
scripts/measure.mjsでライブサイズを取得し、それに合わせて共有部品を構築します。
決定すべきWebflowの動作:
- ページ遷移: 多くのWebflowサイトにはカバー(
.transition+.transition-trigger+ ロード時にトリガーをクリックし、すべての内部リンクで再度クリックし、約1秒後にwindow.locationを設定するjQueryスニペット)があります。オーナーはそれがなくなると気づき、戻してほしいと依頼します。プレーンなCSS + JSで両方の方法で再構築し、動きを減らす設定ではオフにします:- 到着時: カバーが保持され、その後持ち上がります。ライブの遅延とイージングを使用します(エクスポートの
webflow.js内のIX2アクションリストから)。 - 離脱時: 同サイトリンク(
#、新しいタブ、修正されたクリックではない)のクリックにより、カバーがページ上に下りてきて、その後ナビゲートします。IX2の遅延を読み取ります。スライドインが2秒待機する一方でスニペットが1秒後に離脱する場合、ライブ上ではただの無駄な一時停止だけが表示されます。意図されたもの(カバーが約0.8秒で下りる)を構築し、一時停止は構築しません。 - 戻る時:
pageshowでpersistedとともに、カバーを取り除きます。そうしないと、ページが履歴キャッシュからカバーされた状態で戻ってきます。 - 表示場所: 375と1440での計算された
displayを確認します(scripts/measure.mjs)。CSSではありません。カスタム埋め込み(body .transition { display: block })がサイト自身の≤767pxのdisplay: noneを上書きする可能性があるため、電話でも表示されます。
- 到着時: カバーが保持され、その後持ち上がります。ライブの遅延とイージングを使用します(エクスポートの
- Finsweet同意: Consent Mode v2では、Finsweet自身のdataLayerイベント(
<category>-activated)もプッシュします。Finsweet用に構築されたGTMトリガーはその後も作動し続けます。
時間を浪費する2つのCSSの落とし穴:
- bodyのベースサイズ: Webflowの10pxベースフォントサイズは
bodyに置き、決してhtmlには置かないでください。htmlに置くと、1remが10pxになり、すべてのTailwindのスペーシングとサイズが62.5%でレンダリングされます。 - ブレークポイントの混在禁止: 同じファイル内の同じプロパティにremブレークポイント(
md:、lg:)とpxブレークポイント(min-[992px]:)を混在させないでください。Tailwind 4は順序を誤って並べる可能性があります。コンポーネントごとに1種類を使用します。
4. ページ:並行して、ページグループごとに1つのエージェント
各エージェントにreferences/page-brief.md(リポジトリパス、ポート、共有コンポーネント、トークンを記入)と、そのページのルート、head値、セクションリストを渡します。
(以降の手順は長いため、必要に応じてさらに翻訳を提供します。現時点では、上記の部分がマークダウンの大部分をカバーしています。残りの手順も同様の方法で翻訳できます。)