Astro 5からAstro 7へ段階的にアップグレードした


Cloudflare Workersで動かしているこのブログのAstroを、5.16.2から7.1.0へアップグレードしました。

2つのメジャーバージョンをまたぐため、今回は Astro 5 → 6 → 7 の順に更新しています。途中でビルドと型チェックを行い、問題が起きたバージョンを特定しやすくするためです。

この記事では、実際に変更した設定と、作業中に遭遇したエラー、その解決方法をまとめます。

アップグレード前後のバージョン

主なパッケージの変更は次のとおりです。

パッケージ更新前更新後
astro5.16.27.1.0
@astrojs/cloudflare12.6.1214.1.3
@astrojs/mdx4.3.127.0.3
@astrojs/rss4.0.144.0.19
@astrojs/sitemap3.6.03.7.3
wrangler4.56.04.111.0

Astro 7でも既存のRemarkプラグインを使うため、@astrojs/markdown-remark7.2.1も追加しました。

更新前にnpm auditを実行すると、wranglerminiflareが依存するundiciwsに、Highを含む7件の脆弱性が検出されました。アップグレード後は0件になっています。

事前確認

Node.jsのバージョン

Astro 6以降はNode.js 22.12.0以上が必要です。ローカルだけでなく、CIやデプロイ環境も確認します。

node -v

今回の環境はv22.20.0だったため、そのまま進められました。

破壊的変更の影響範囲

Astro 6では古いContent Collections APIが削除され、Content Layer APIへの移行が必須です。このブログではすでにsrc/content.config.tsglob()ローダーを使っていたため、大きな移行は不要でした。

import { glob } from "astro/loaders";
import { defineCollection } from "astro:content";

const blog = defineCollection({
  loader: glob({
    base: "./src/content/blog",
    pattern: "**/*.{md,mdx}",
  }),
  // ...
});

次のような古いAPIを使っている場合は、先に置き換える必要があります。

  • entry.slug
  • entry.render()
  • getEntryBySlug()
  • Astro.glob()
  • type: "content"を使った旧コレクション定義

公式の移行ガイドも確認しておきます。

作業ブランチを作る

アップグレード専用のブランチを作りました。

git switch -c upgrade/astro-7

作業ツリーがクリーンな状態で始めると、package-lock.jsonを含む変更を追いやすくなります。

Astro 5から6へアップグレード

まずAstro 6系と、対応する公式インテグレーションへ更新しました。

npm install \
  astro@6.4.8 \
  @astrojs/cloudflare@13.7.0 \
  @astrojs/mdx@6.0.3 \
  @astrojs/rss@4.0.19 \
  @astrojs/sitemap@3.7.3 \
  wrangler@4.111.0

Wranglerも同時に上げる

最初はwranglerを古い4.56.0のままにしていたため、インストール時にERESOLVEが発生しました。

peer wrangler@"^4.83.0" from @astrojs/cloudflare@13.7.0

@astrojs/cloudflare@13.7.0wrangler@^4.83.0を要求します。今回は脆弱性もまとめて解消するため、最新の4.111.0へ更新しました。

--force--legacy-peer-depsで依存関係の不整合を隠すのではなく、要求されるバージョンに揃えています。

Zodのimport先を変更する

Astro 6ではContent Collectionsのスキーマに使うZodがv4になりました。zのimport先をastro:contentからastro/zodへ変更します。

import { glob } from "astro/loaders";
import { defineCollection } from "astro:content";
import { z } from "astro/zod";

今回のスキーマではz.coerce.date()などがそのまま動きましたが、Zod 4では変換後の型やデフォルト値の扱いが変わるケースがあります。複雑なスキーマを使っている場合は、ビルド時のバリデーション結果も確認した方が安全です。

Cloudflareアダプターのエントリーポイントを変更する

Astro 6対応のCloudflareアダプターでは、Wranglerのmainがビルド成果物ではなく、アダプターが提供する共通エントリーポイントを指すようになりました。

{
  "main": "@astrojs/cloudflare/entrypoints/server"
}

変更前の設定を残すと、ビルド時に次のエラーが発生します。

The provided Wrangler config main field
(.../dist/_worker.js/index.js) doesn't point to an existing file

platformProxyを削除する

以前の設定ではCloudflareアダプターにplatformProxyを渡していました。

adapter: cloudflare({
  platformProxy: {
    enabled: true,
  },
}),

Astro 6対応版ではこのオプションが存在しないため、単純な呼び出しに変更します。

adapter: cloudflare(),

残したままだと、型チェックで次のエラーになります。

Object literal may only specify known properties,
and 'platformProxy' does not exist in type 'Options'.

ここまででAstro 6のビルドと型チェックを実行し、いったんコミットしました。

npm run build
npx tsc --noEmit
git commit -am "chore: upgrade to Astro 6"

Astro 6から7へアップグレード

Astro 6で正常に動くことを確認してから、Astro 7と対応パッケージへ進みます。

npm install \
  astro@7.1.0 \
  @astrojs/cloudflare@14.1.3 \
  @astrojs/mdx@7.0.3 \
  @astrojs/markdown-remark@7.2.1

既存のRemarkプラグインを維持する

Astro 7のデフォルトMarkdownプロセッサーは、Rust製のSätteriに変更されました。

このブログでは次のRemarkプラグインを使っています。

  • YouTube URLをiframeへ変換する独自プラグイン
  • URLをリンクカードに変換するremark-link-card
  • Mermaidコードブロックを処理するastro-mermaid

既存のRemark/Rehypeパイプラインを維持するため、@astrojs/markdown-remarkunified()を明示的に設定しました。

import { unified } from "@astrojs/markdown-remark";

export default defineConfig({
  markdown: {
    processor: unified({
      remarkPlugins: [
        remarkYouTube,
        [
          remarkLinkCard,
          {
            cache: true,
            shortenUrl: true,
          },
        ],
      ],
    }),
  },
});

markdown.remarkPluginsへ直接指定する形式も一時的には動作しますが、Astro 7では非推奨警告が出ます。プラグインをunified()へ渡すことで警告を解消できます。

Rustコンパイラーによるチェック

Astro 7ではRust製コンパイラーがデフォルトかつ唯一のコンパイラーになりました。閉じタグの不足など、以前は許容されていた無効なHTMLがエラーになる可能性があります。

今回のビルドでは.astroファイルの修正は不要でしたが、メジャーアップデート後は全ページをビルドし、可能なら表示も確認するのが安全です。

CloudflareでHTMLが[object Object]になる問題

Astro 7への更新後、ビルド自体は成功しました。しかし、Cloudflareアダプターを通して生成されたHTMLを確認すると、全ページが次の15文字だけになっていました。

[object Object]

Cloudflareアダプターを外すと正常なHTMLが生成されたため、Markdownやページ実装ではなく、Workersランタイムとの互換性が原因だと切り分けられました。

これはnodejs_compatで有効になるprocess v2をAstroがNode.js環境として誤検出し、WorkersのResponseが扱えない形式で本文を返す既知の問題です。

今回はwrangler.jsondisable_nodejs_process_v2を追加しました。

{
  "compatibility_date": "2026-07-17",
  "compatibility_flags": [
    "nodejs_compat",
    "disable_nodejs_process_v2"
  ]
}

この変更後、Cloudflareアダプターを有効にした状態でも完全なHTMLが生成されるようになりました。

新しいcompatibility_dateでは別のランタイム機能によって解消される場合もありますが、アップデート後はビルドの終了コードだけでなく、生成されたHTMLやプレビューのレスポンス本文まで確認した方が安全です。

ヒーロー画像が消える問題

デプロイ後、記事のヒーロー画像が表示されなくなりました。

原因は、@astrojs/cloudflareのデフォルト画像サービスがcompile(ビルド時変換)からcloudflare-binding(ランタイム変換)へ変わったことです。HTML上の画像URLが次のような形式になります。

<img src="/_image?href=%2F_astro%2Fhero....png&w=1020&h=510" ...>

静的アセットへの直接参照ではなく、Workers経由の画像変換エンドポイントを使うため、バインディングやランタイムの条件によっては画像が壊れます。

このブログは静的配信が主なので、ビルド時に画像を変換する設定へ戻しました。

adapter: cloudflare({
  imageService: "compile",
}),

再ビルド後は、従来どおり静的ファイルを参照するURLになります。

<img src="/_astro/hero.....webp" ...>

静的アセットの出力先を更新する

新しいCloudflareアダプターでは、静的アセットがdist/clientへ出力されます。Wranglerの設定も合わせて変更しました。

{
  "assets": {
    "directory": "./dist/client",
    "binding": "ASSETS"
  }
}

最終的なwrangler.jsonの要点は次のとおりです。

{
  "name": "blog",
  "compatibility_date": "2026-07-17",
  "compatibility_flags": [
    "nodejs_compat",
    "disable_nodejs_process_v2"
  ],
  "main": "@astrojs/cloudflare/entrypoints/server",
  "assets": {
    "directory": "./dist/client",
    "binding": "ASSETS"
  }
}

最終確認

最後に、ビルド、型チェック、デプロイのドライランを実行しました。

npm run build
npx tsc --noEmit
npx wrangler deploy --dry-run
npm audit

確認結果は次のとおりです。

  • 全19ページの静的生成に成功
  • TypeScriptのエラーなし
  • Wranglerのドライラン成功
  • YouTube埋め込みが生成されたHTMLに存在
  • リンクカードが生成されたHTMLに存在
  • 各記事のHTMLが[object Object]ではなく正常な内容を持つ
  • npm auditの脆弱性は0件

Remark Link Cardが外部サイトからOpen Graph情報を取得できず、警告を出すURLはありました。ただし、これはアップグレード前からある外部サイト側の応答によるもので、ビルドは成功しています。

まとめ

Astro 5から7へのアップグレードは、Content Layer APIへ移行済みであればコード本体の変更は多くありませんでした。一方で、Cloudflare Workersと組み合わせる場合は、次の設定変更が重要でした。

  1. Astroと公式インテグレーションを対応するメジャーバージョンへ揃える
  2. wranglerのpeer dependencyも同時に更新する
  3. Zodをastro/zodからimportする
  4. WranglerのmainをCloudflareアダプターの共通エントリーポイントへ変更する
  5. Remarkプラグインをunified()プロセッサーへ移す
  6. nodejs_compatによる[object Object]問題をランタイム設定で回避する
  7. 画像サービスをimageService: "compile"にしてビルド時変換へ戻す
  8. dist/clientを静的アセットの出力先として確認する

メジャーバージョンを一度に上げること自体よりも、各段階でビルド・型チェック・生成物の確認を行うことが重要でした。特に今回の[object Object]問題はビルドが成功していたため、終了コードだけを見ていたら見逃していた可能性があります。