メインコンテンツへスキップ
#Web制作 約8分で読めます

Astro 7.2のインクリメンタルビルドを入れたら、削減幅は90%ではなく4割だった

Astro 7.2のexperimental.incrementalBuildを実際に設定して、ビルド時間がどれだけ縮むか検証した記録。cacheKeyの設計で踏んだ落とし穴も書く。

「差分ビルドで90%削減」という触れ込みを見て、すぐに試した。結論から言うと、手元の環境では4割弱の短縮だった。それでも十分ありがたいが、数字が独り歩きする前に実測を残しておく。

何が変わったのか

Astro 7.2(2026年8月6日リリース)で、experimental.incrementalBuild フラグが追加された。やっていることは単純で、前回ビルドの出力をキャッシュしておき、コードもデータも変わっていないページはレンダリングをスキップして前回の成果物をそのままコピーする。

対象は getStaticPaths() で生成される静的ページだけ。各パスに cacheKey という文字列を返すと、Astro側がそのキーとモジュール依存グラフのハッシュを突き合わせて、両方一致すればスキップ、どちらか変わっていれば再レンダリングする(公式ドキュメント)。

設定はこれだけ。

// astro.config.mjs
import { defineConfig } from "astro/config";

export default defineConfig({
  experimental: {
    incrementalBuild: true,
  },
});

cacheKeyをどう設計するかで結果が変わる

ここが一番迷った。Content Collectionsを使っているなら entry.digest をそのまま返せばいい。loaderがコンテンツの変更を追跡してdigestを更新してくれる。

export async function getStaticPaths() {
  const posts = await getCollection("blog");
  return posts.map((post) => ({
    params: { slug: post.slug },
    props: { post },
    cacheKey: post.data.digest,
  }));
}

問題は、記事本文以外のデータに依存しているページ。たとえば「関連記事」をサイドバーに出しているページは、自分の記事が変わっていなくても関連記事側が変われば表示が古くなる。cacheKey に記事のdigestだけ入れていると、関連記事の更新を検知できずにキャッシュが使われてしまう。

最初これに気づかず、1記事を更新したのにサイドバーの関連記事リンクが前回ビルドのまま残った。cacheKeyに関連記事のdigestも混ぜるか、いっそ関連記事セクションを持つページは cacheKey を返さない(=毎回レンダリング)かの二択になる。自分は後者を選んだ。全ページをスキップ対象にしようとすると、かえってキャッシュの正しさを担保する設計コストが上がる。

手元の計測結果

ブログ記事が3言語で約200ページ、ドキュメントとLPを含めて合計400ページほどのサイトで試した。

1記事だけ修正してビルドした場合、cacheKeyを設定したページの大半がスキップされ、ビルド時間は体感で3〜4割短くなった。ページ数がもっと多いサイト(600〜700ページ規模)だと、ある開発者が約39%の短縮を報告している。661ページ中637ページがキャッシュから復元され、再レンダリングは24ページだけだったという。

「90%削減」を達成するには、ページ数が数千規模で、かつ変更が本当に1〜2ページだけ、という条件が必要になる。レンダリングのスキップ率と、レンダリング以外の処理(アセット処理、ルーティング解決など)の比率次第で上限が決まるので、全サイトで90%減るわけではない

concurrency: 1 の制約に注意

試していて引っかかったのが、build.concurrency との排他制約。インクリメンタルビルドは concurrency: 1(逐次レンダリング)でしか動かない。並列レンダリングを有効にしていると、キャッシュが無効化される旨の警告が出て、普通のフルビルドにフォールバックする。

GitHub Issue #17613で2万ページ超のサイトの開発者がこの制約について報告している。原因はキャッシュメタデータの追跡がプロセスグローバルなシングルトンに依存しているためで、並列レンダリングすると記録が混線する。ページ数が多いほど並列化の恩恵も大きいので、ここは今後の改善待ち。

CIでキャッシュを持ち回す

ローカル開発なら node_modules/.astro/ が勝手に残るが、CIでは毎回消えるのでキャッシュを明示的に保存・復元する必要がある。GitHub Actionsなら actions/cache でこのディレクトリを指定する。

- uses: actions/cache@v4
  with:
    path: node_modules/.astro
    key: astro-build-${{ hashFiles('astro.config.mjs') }}-${{ github.sha }}
    restore-keys: |
      astro-build-${{ hashFiles('astro.config.mjs') }}-

astro.config.mjs が変わったらキャッシュを捨てるようにしている。設定変更はキャッシュ全体を無効化するので、古いキャッシュを引いても意味がない。

もうひとつ、画像インポートが複数あるページで依存ハッシュが非決定的に変わるバグがあった(#17615)。Rolldownの emitFile() が返すハンドルの順序が不安定だったのが原因で、7.2.1で修正済み。CIでキャッシュヒット率が不自然に低いと感じたら、まずバージョンを確認するのがいい。

ミドルウェアの変更は検知されない

公式ドキュメントにさらっと書いてあるが、ミドルウェアのコード変更はキャッシュを無効化しない。レスポンスヘッダの追加やリダイレクト処理をミドルウェアで変えたあとは astro build --force でフルビルドを走らせる必要がある。最初これを見落として、ステージング環境でヘッダ変更が反映されず数十分悩んだ。

検証環境: Ubuntu 22.04 / Node.js 22.8.0 / Astro 7.2.1 / 検証日 2026-09-05

参考リンク

#Astro #インクリメンタルビルド #ビルド最適化 #静的サイト #CI/CD
シェア