AstroをCloudflare Pagesへデプロイする|静的サイトの最短手順と詰まりどころ

Astroの静的サイトをCloudflare PagesへGitHub Actionsから配信する手順を、設定、権限分離、404・canonical・sitemapの検査まで順に解説します。

Astroで作ったサイトをCloudflare Pagesへ載せるときは、いきなり配信コマンドを実行するのではなく、静的ファイルを正しく生成できる状態を先に固定するのが近道です。基本の流れは「Astroでdistを作る」「検品する」「合格したdistだけをPagesへ送る」の3段階です。

この記事では、既存のCloudflare Pagesプロジェクト、またはPagesを明示的に選ぶサイトを対象に、GitHub Actionsで検品したあとWranglerでDirect Uploadする構成を扱います。設定値はすべてダミーです。

2026年7月23日時点では、Astroの公式ガイドはCloudflareの新規プロジェクトにWorkersを案内しています。一方、静的なdistをPagesへDirect Uploadする公式手順も提供されています。新規採用では両者を比較し、Pagesを使うと決めた場合に以下の手順を適用してください。サイト作成の段階で迷っている場合は、先にAstroでMarkdown記事サイトを作る最小構成を確認すると、ビルド対象を整理できます。

1. static出力を確認する

Astroのoutputは既定でstaticです。原則としてページをビルド時にHTMLへ変換するため、実行時のサーバー処理が不要な記事サイトに向きます。astro.config.mjsは次の最小形を確認します。

import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://example.com',
  output: 'static',
  integrations: [sitemap()],
});

一部ルートをオンデマンド描画にしている場合は、staticでも完全な静的サイトとは限りません。現在の@astrojs/cloudflareアダプターはCloudflare Pagesへのデプロイをサポートしていないため、SSRなど実行時処理が必要ならWorkersを検討します。astro buildの既定出力先はdist/です。

npm ci
npm run build

ビルド後はdist/index.htmlと主要ページのHTMLを見ます。「成功」と表示されても、必要なURLに対応するファイルがなければ配信へ進みません。

2. site URLを設定する

siteには本番URLをhttps://から指定します。Astroはこの値をサイトマップや完全なURLの生成に使います。開発用URLを残すと、ページが表示できてもcanonicalやサイトマップが別ドメインを指すことがあります。

独自レイアウトでは、siteを設定しただけでcanonicalタグが必ず入るとは考えず、Astro.siteなどから<link rel="canonical">を組み立てます。@astrojs/sitemapを使うなら、robots.txtから/sitemap-index.xmlも案内します。設定変更後は再ビルドし、HTMLとXMLの実値を確認します。

3. Pagesプロジェクトを用意する

Cloudflare Pagesには、Cloudflare側でGitリポジトリをビルドする方法と、別のCIで作った成果物をDirect Uploadする方法があります。この記事は後者です。初回はダッシュボードまたは次のWranglerコマンドでプロジェクトを作ります。

npx wrangler pages project create

配信先は通常<PROJECT_NAME>.pages.devです。Direct Uploadとして作成したプロジェクトは、あとからGit integrationへ切り替えられません。方式を決めてから本番プロジェクトを作ります。

2026年7月23日の公式上限表では、Freeプランは月500ビルド、同時ビルド1件、1サイト20,000ファイル、1ファイル25 MiBです。静的アセットへのリクエストは無料・無制限ですが、Pages FunctionsはWorkers側の枠に数えられます。公開直前にも再確認してください。

配信方式を比較する

選択肢ビルドする場所配信のきっかけ向くケース変更時の注意
Pages Direct UploadGitHub Actionsなど任意のCIwrangler pages deploy dist独自の検品後に成果物だけを送る作成後にGit integrationへ切り替えられない
Pages Git integrationCloudflareGitHub・GitLabへのpushCloudflare側でビルドから配信まで完結させる作成後にDirect Upload方式へ切り替えられない
Workers Static Assets任意のCIまたはCloudflarewrangler deployなど新規案件や、静的配信と実行時処理を同じ基盤で扱うPagesとは設定と配信コマンドが異なる

この記事の構成は、ビルド後に独自のリンク検査やHTML検査を挟みたい場合に適しています。Cloudflareだけで完結させたい場合はGit integration、新規案件でオンデマンド描画も必要ならWorkersを候補にします。

4. GitHub Actionsから配信する

配信ジョブは「取得、Node.js準備、依存復元、ビルド、検品、配信」の順にします。次のEXAMPLE_PROJECTは実際のプロジェクト名へ置き換えます。

name: Deploy Pages

on:
  push:
    branches: [main]
  workflow_dispatch:

concurrency:
  group: pages-production
  cancel-in-progress: true

permissions:
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: '24'
          cache: 'npm'

      - run: npm ci
      - run: npm run build
      - run: npm run check

      - name: Cloudflare Pagesへ配信
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: pages deploy dist --project-name=EXAMPLE_PROJECT

npm run checkは例なので、実在する型検査やリンク検査へ置き換えます。処理が止まったら再実行を連打せず、停止原因を切り分ける手順で最後の成功工程と終了コードを確認します。

concurrencyは配信の重複を防ぎます。例では新しい実行が古い実行を取り消すため、途中キャンセルが危険な処理にはそのまま使いません。

5. secretsと権限を分ける

Cloudflareのトークン値やAccount IDをYAMLやログへ直書きせず、GitHubのRepository secretsまたはEnvironment secretsへ登録します。

  • CLOUDFLARE_API_TOKEN
  • CLOUDFLARE_ACCOUNT_ID

カスタムAPIトークンにはCloudflare PagesのEdit権限が必要です。Global API Keyではなく、対象アカウントと用途を絞ります。GitHub側もcontents: readから始め、GitHub Deploymentsを使う場合だけdeployments: writeを足します。

ログの自動伏字は完全ではありません。トークンをechoせず、未信頼のPull Requestで本番Secretを使わず、漏えいの疑いがあれば失効・再発行します。運用全体は毎日の作業を自動化する考え方も参考になります。

6. 404・canonical・sitemapを検査する

デプロイ成功の表示だけでは完了ではありません。公開URLで次を確認します。

  1. トップと代表的な記事が200で表示される
  2. 存在しないURLで意図した404ページになる
  3. HTMLのcanonicalが本番ドメインと正しいパスを指す
  4. /sitemap-index.xmlを取得でき、公開ページのURLが本番ドメインになっている
  5. robots.txtにサイトマップの正しいURLがある
  6. CSSや画像のURLが404になっていない

確認はブラウザ表示だけでなく、レスポンスとHTMLも見ます。

curl -I https://example.com/
curl -I https://example.com/not-found-check
curl -s https://example.com/ | grep canonical
curl -s https://example.com/sitemap-index.xml

PowerShellでは必要に応じてcurl.exeまたはInvoke-WebRequestを使います。canonicalはURLまで、404は見た目だけでなくHTTPステータスまで検査します。

問題があればdist内にも同じ欠陥があるか確認します。distが誤っていればAstro側、公開先だけ誤っていればプロジェクト名、キャッシュ、独自ドメイン設定へ範囲を狭めます。

FAQ

静的サイトでも@astrojs/cloudflareは必要ですか?

Pagesへ完成済みのdistをDirect Uploadするだけなら不要です。Astroの通常の静的ビルドを行い、出力されたdistをWranglerで配信します。オンデマンド描画が必要なら、現在はCloudflare Workers向けの構成を検討します。

Direct UploadからGit integrationへ後で変更できますか?

同じPagesプロジェクトの方式は後から切り替えられません。Git integrationが必要になった場合は、新しいプロジェクトを作る前提でドメイン移行や切り戻し手順を準備します。

デプロイ成功後に一部のページだけ404になるのはなぜですか?

まず対象URLに対応するHTMLがdist内にあるか確認します。なければAstroのルート生成、あれば配信先のプロジェクト名、パス、独自ドメイン設定を調べます。Astroサイトのリンク切れを自動検査する方法も併用できます。

siteを設定すればcanonicalも自動で入りますか?

siteは絶対URL生成の基準ですが、独自レイアウトへcanonicalタグを自動挿入する設定ではありません。生成後のHTMLを開き、hrefが本番URLを指すことを確認します。実装例はcanonical URLの書き方で確認できます。

GitHub ActionsのSecretがあればログ流出を防げますか?

GitHubは登録済みSecretの伏字処理を行いますが、値の変換方法などによって完全に隠れるとは限りません。ログへ出力しない設計を基本にし、Cloudflareトークンの対象アカウントと権限も必要最小限に絞ります。

関連記事

調査メモ

確認日: 2026-07-23

続き(結論と実データ)はnoteに置いています

ここでは手順のところまで書きました。実際に出た数字、うまくいかなかった条件、そのまま使える設定ファイルは、 note の記事にまとめてあります。

noteで続きを読む Xをフォローする