個人ブログを Astro 6 から 7 にアップグレードした

個人ブログを Astro 6 から 7 にアップグレードした

blog.imaikosuke.com を Astro 6 から 7 に上げた記録。Cloudflare アダプタの dev 設定(imageService / prerenderEnvironment)と、pnpm build の実測ベンチマーク。24 ページで Cold build が 11 秒台→2 秒台(−79%)、100 記事シミュレーションでは Warm build が −78% 改善。Sätteri ではなく unified を維持した構成でも、Vite 8 + Rolldown・Rust コンパイラ・Queued Rendering で十分速くなった理由をまとめました。

12min

blog.imaikosuke.com は Astro 6 で運用していました。Astro 7 がリリースされたタイミングで、自分の個人ブログも v7 に上げたので、その記録です。

変更内容

アップグレード自体は公式の自動化 CLI を使いました。

Terminal window
pnpm dlx @astrojs/upgrade

実行すると依存パッケージの差分を見せてくれて、breaking change がある旨を確認してから一括更新してくれる。今回はこんな感じで更新されました。

パッケージbeforeafter
astro6.4.77.0.3
@astrojs/cloudflare13.7.014.0.1
@astrojs/check0.9.80.9.9
@astrojs/sitemap3.7.23.7.3

astro@astrojs/cloudflare がメジャーアップされて、私の環境では package.json のバージョン更新だけでは足りず、Cloudflare アダプタの設定変更も必要でした。

Markdown プロセッサは unified のまま維持

Astro 7 では Markdown / MDX のデフォルト処理系が Rust 製の Sätteri(新しいタブで開く) に置き換わった。ただしこのブログは remark-link-card-plus をはじめとする remark / rehype プラグインに依存しているため、Sätteri ではなく従来の unified パイプラインを使い続ける必要がありました。

@astrojs/markdown-remarkunified プロセッサを markdown.processor に明示的に渡すだけで、これまで通りのプラグイン構成を維持しています。

astro.config.ts
import { unified } from '@astrojs/markdown-remark'
// ...
markdown: {
processor: unified({
remarkPlugins: [
remarkEmbeds,
remarkReadingTime,
[
remarkLinkCard,
{
cache: true,
ogTransformer: (og: OgData) => ({
...og,
imageUrl: optimizeLinkCardOgImageUrl(og.imageUrl)
})
}
],
remarkLinkCardImages
],
rehypePlugins: [rehypeCleanup, rehypeImageProcessor, rehypeMarkdownExternalLinks]
})
},

つまり、残念ながらこのブログでは Sätteri の恩恵を今回は受けていないです。それでも後述の通り、ビルドは想定以上に速くなりました。

Cloudflare アダプタ

@astrojs/cloudflare 14(Astro 7)では、astro dev で adapter の出し分けが必要になりました。

dev で画像の cloudflare:workers import に失敗する

画像付きの記事を開くと、ページ自体は 200 でも画像最適化リクエスト(/_image?...)が裏で失敗した。

[ERROR] Could not import `cloudflare:workers`.
at node_modules/@astrojs/cloudflare/dist/entrypoints/image-transform-endpoint.js:1:21

imageService: 'compile' でも dev 時の画像変換エンドポイントが Workers 専用モジュールを import するようになったのが原因でした。

@astrojs/cloudflare/dist/entrypoints/image-transform-endpoint.js
import { env } from 'cloudflare:workers'

cloudflare:workers は Workers ランタイム専用の仮想モジュールで、astro dev(Node + Vite)では解決できない(withastro/astro#13523(新しいタブで開く))。アダプタ 13(Astro 6)の dev では汎用エンドポイントが使われていたため、v7 で初めて表面化した。

dev で Markdown の編集が反映されない

リンクカード(remark-link-card-plus)を含め、記事 Markdown を保存してもブラウザに反映されないことがあった。データストア(.astro/data-store.json)は更新されているのに、配信 HTML が古いままになる。

@astrojs/cloudflare 14 の dev では、記事ページ(プリレンダー対象)が prerender 環境経由で配信される。prerenderEnvironment: 'workerd' にすると Astro のコンテンツ無効化が効き、Markdown HMR が正常に動く(withastro/astro#16567(新しいタブで開く) と同種の問題)。

prerenderEnvironment は、プリレンダー対象ページを どのランタイムで HTML 化するか を指定するオプション。

  • 'node': Node.js 上で HTML を生成する。pnpm build ではこちらを使う。Node 上で一括ビルドするのが速く、本番デプロイ前の成果物生成に向いている。
  • 'workerd': Cloudflare Workers 互換の workerd(新しいタブで開く) 上で HTML を生成する。astro dev では本番に近い実行環境になる。Astro 6 系の dev と同様にコンテンツ変更の検知(HMR)が効く。

dev で 'node' のままだと、データストアは更新されるのに配信 HTML が古いまま、というズレが起きる。

astro.config.ts の adapter 設定

上の 2 点は、adapter を dev / build で出し分ける 1 つの設定にまとめました。

astro.config.ts
adapter: cloudflare({
imageService: import.meta.env.DEV ? 'passthrough' : 'compile',
prerenderEnvironment: import.meta.env.DEV ? 'workerd' : 'node'
}),
設定devbuild
imageServicepassthrough(変換なし・cloudflare:workers を import しない)compile(ビルド時に画像最適化)
prerenderEnvironmentworkerd(Workers 互換ランタイム・Markdown HMR が効く)node(Node.js で一括ビルド)

dev では画像は元の URL のまま配信される。最適化後の見た目を確認したいときは pnpm build / pnpm preview で見る。リンクカードも、dev サーバー再起動なしで反映される。

参考

ビルド時間

v7 でもビルド性能の改善が大きなテーマなので、pnpm build の時間を計測してみました。公式ベンチマークの数字より、自分のプロジェクトで実際にどれだけ変わるかが気になったからです。

計測環境

項目
計測日v6: 2026-06-23 / v7: 2026-06-26
マシンMacBook Air(Apple M5)
CPU10 cores(4 performance + 6 efficiency, arm64)
メモリ32 GB
Nodev24.16.0

計測方法

  • Cold build: dist / .astro / node_modules/.vite を削除してから 1 回実行
  • Warm build: キャッシュを残したまま 5 回連続実行し、平均・最小・最大を記録
  • wall clock: /usr/bin/time -preal(pnpm 起動や pagefind インデックスなども含む実経過時間)
  • Astro ログ: N page(s) built in Xs の X(Astro が報告するビルド時間)

アップグレード前(Astro 6.4.7)

Cold build

指標秒数
wall clock (real)11.04
Astro ログ8.81

内訳(Astro ログより):

  • Content sync / types 生成: 5.80s
  • Static routes 生成: 1.71s
  • 画像最適化: 10ms(キャッシュ再利用)

Warm build(5 回)

指標平均最小最大
wall clock (real)3.953.844.15
Astro ログ2.862.802.92
runwall clockAstro ログ
warm-14.152.85
warm-23.882.86
warm-33.922.87
warm-43.842.80
warm-53.952.92

アップグレード後(Astro 7.0.3)

Cold build

指標秒数
wall clock (real)2.36
Astro ログ1.29

内訳(Astro ログより):

  • Content sync / types 生成: 0.23s
  • Static routes 生成: 0.48s
  • 画像最適化: 1ms(キャッシュ再利用)

Warm build(5 回)

指標平均最小最大
wall clock (real)2.062.042.08
Astro ログ1.011.001.02
runwall clockAstro ログ
warm-12.071.01
warm-22.051.01
warm-32.041.02
warm-42.081.00
warm-52.071.01

比較まとめ

指標v6.4.7 (before)v7.0.3 (after)改善率
Cold wall clock11.04s2.36s−79%
Warm wall clock 平均3.95s2.06s−48%
Warm Astro ログ 平均2.86s1.01s−65%

100 記事シミュレーション(v6 vs v7)

記事数が増えた場合の改善率を見るため、文字数の多い blog-tech-stack を 95 本複製し、既存の公開記事 5 本と合わせて 100 記事(130 ページ)の状態で v6 / v7 を同条件比較しました。

項目
記事数100(blog-tech-stack 複製 95 + 既存 5)
ページ数130
v6Astro 6.4.7
v7Astro 7.0.3

アップグレード前(Astro 6.4.7 / 100 記事)

Cold build
指標秒数
wall clock (real)36.45
Astro ログ35.02
Warm build(5 回)
指標平均最小最大
wall clock (real)11.1310.9811.38
Astro ログ10.039.8810.23
runwall clockAstro ログ
warm-111.3810.23
warm-210.989.88
warm-311.2010.11
warm-411.069.98
warm-511.049.95

アップグレード後(Astro 7.0.3 / 100 記事)

Cold build
指標秒数
wall clock (real)27.63
Astro ログ25.89
Warm build(5 回)
指標平均最小最大
wall clock (real)2.402.312.50
Astro ログ1.331.261.39
runwall clockAstro ログ
warm-12.501.39
warm-22.381.33
warm-32.361.30
warm-42.451.37
warm-52.311.26

100 記事での比較まとめ

指標v6.4.7 (before)v7.0.3 (after)改善率
Cold wall clock36.45s27.63s−24%
Warm wall clock 平均11.13s2.40s−78%
Warm Astro ログ 平均10.03s1.33s−87%

5 記事(24 ページ)のときの Warm は v6 3.95s → v7 2.06s(−48%)だったけど、100 記事だと v6 の Warm が 11 秒台になりました。v7 は 2 秒台前半のまま。記事が増えるほど差が広がるのがわかります。

なぜ速くなったのか

公式アナウンス(新しいタブで開く)を読むと、Astro 7 は「とにかく速さに振った」リリースで、ビルドのボトルネックだった処理を Rust 製の実装へ置き換えたのが大きい。具体的には、JS バンドル(esbuild + Rollup → Rolldown)、.astro コンパイラ(Go → Rust)、Markdown 処理(unified → Sätteri)の 3 層。公式は 15〜61% の改善と書いているが、自分の計測ではそれを超える改善幅が出た(Cold −79%、100 記事 Warm −78%)。自分の環境で効いたのは主に次の 3 点だと思っています。

1. Vite 8 + Rolldown

Astro 7 は Vite 8(新しいタブで開く) にアップグレードされ、バンドラーが Rust 製の Rolldown(新しいタブで開く) に置き換わったこと。これまで esbuild(トランスパイル)と Rollup(バンドル)に分かれていた処理が、Rolldown 一つに統合された。Rolldown は Rollup 比で 10〜30x 高速とされている。

Astro のビルドは大きく「① JS バンドル」「② HTML 生成」の 2 段階で、Rolldown が効くのは ① のバンドル工程。プラグイン API は Rollup 互換なので、バンドルまわりの Vite 設定は無縁校のまま Rolldown の恩恵を受けられました。

2. Rust 製 .astro コンパイラ

.astro のコンパイラが Go から Rust へ書き換えられたこと。単体でいうと docs.astro.build で約 6% 程度の改善らしいけど、他の改善と合わさって効いてくる。

このブログで一番体感が大きかったのが、Cold build の Content sync / types 生成が 5.80s → 0.23s(−96%) に縮んだこと。ここがネイティブ化された影響が、Cold build の短縮(11.04s → 2.36s)に直結している。

3. Queued Rendering

ビルド時、Astro は各ページのコンポーネントツリーを HTML 文字列に変換する(前述の ② の工程)。Astro 7 では、この HTML 生成のやり方が Queued Rendering(新しいタブで開く) に変わった。

従来は「親コンポーネントを描画 → 子を 1 つずつ再帰的に描画 → その子の子を…」と、関数の入れ子呼び出しで木を下っていく方式だった。コンポーネントのネストが深いページほど、呼び出し回数とメモリの消費が増える。

Queued Rendering では、描画待ちのノードを キュー(スタック)に積んで、1 本の while ループで順に処理 する。再帰呼び出しをやめたことで、同じ HTML 生成をより少ないオーバーヘッドで回せる。Astro 6.0 で experimental として入っていた機能が 7.0 でデフォルトになった。公式によると約 2.4× 速く、メモリ消費も減っているらしい。

Sätteri を使わなくても十分速い

前述の通り、Sätteri の恩恵はこのブログでは受けていない。

それでも Vite 8 + Rolldown / Rust コンパイラ / Queued Rendering の 3 つだけで Cold −79%・Warm −48〜78% が出た。プラグイン資産を優先して Sätteri を諦めた形だけど、それでもここまで速くなっている。

まとめ

pnpm dlx @astrojs/upgrade を実行して、Astro 6.4.7 → 7.0.3 に上げた。Markdown は unified パイプラインのまま。Cloudflare デプロイは dev の adapter 周り(imageService の dev / build 出し分け、prerenderEnvironment: 'workerd')で手こずった。

ビルドはここまで速くなった。

  • 24 ページ(現状): Cold build 11.04s → 2.36s(−79%)
  • 100 記事シミュレーション: Warm build 11.13s → 2.40s(−78%)

Sätteri を使っていないのに Cold で −79% 出たのは驚いた。記事が増えるほど Warm での差が広がるのもわかったので、書けば書くほど恩恵が大きくなる。今後は Sätteri への移行で、さらなる高速化を考えていきたい。

imaikosuke

imaikosuke

Tokyo-based software engineer. Writing about tech and daily learnings.

@imaikosuke_