Skip to content
loke.dev
Blank page tiles move along a conveyor toward a cyan archive slot while an amber fingerprint tile is diverted.

Fix Astro 7.2 Incremental Builds Not Restoring Pages

Find why Astro 7.2 re-renders unchanged pages when the Fonts API is enabled, verify the dependency hash, and choose a safe temporary workaround.

Published Updated 5 min read

Astro 7.2 can finish an incremental build successfully while restoring none of the pages you expected it to cache. If the affected pages render Astro's <Font> component, a changing font-server address can make their dependency hash different on every build.

The safe temporary fix is to keep one of the two features, not both: remove experimental.incrementalBuild if you need the Fonts API, or return to a tested non-Astro font setup if incremental builds matter more. Before changing either, run the two-build check below so you do not mistake a missing CI cache or an invalid cache key for this bug.

First prove that the page is eligible for reuse

Astro's incremental build documentation says a page can be skipped only when it comes from getStaticPaths(), includes a cacheKey, and has unchanged data and code. The cache is stored in cacheDir, which defaults to node_modules/.astro/. CI must restore that directory before the next build.

Check these conditions before blaming the Fonts API:

  • The project is using Astro 7.2.0 with experimental.incrementalBuild enabled.
  • The route is returned from getStaticPaths() with a cacheKey that is the same on both builds.
  • build.concurrency is 1 or unset. Values above 1 disable this cache and produce a warning.
  • The second build keeps cacheDir and does not use astro build --force.
  • No source file, Astro configuration, dependency, or lockfile changed between builds.
  • The page or one of its layouts renders <Font> from astro:assets.
import { defineConfig } from "astro/config";

export default defineConfig({
  experimental: {
    incrementalBuild: true,
  },
});
---
export function getStaticPaths() {
  return [{
    params: { slug: "example" },
    cacheKey: "unchanged",
  }];
}
---

<h1>Unchanged page</h1>

Run two builds without deleting dist or cacheDir between them. The second log should mark the eligible route as restored. If every route renders again, inspect node_modules/.astro/incremental-build.json and compare the route's dependencyHash across the two runs.

npm run build
# Save the route's dependencyHash from:
# node_modules/.astro/incremental-build.json

npm run build
# Compare the same route and look for "(restored)" in the build log.

The comments above are a diagnostic recipe, not a portable script. Route keys match source file paths, so use the key that appears under routes in your own manifest.

What the Fonts API changes

Astro's Fonts API guide configures a font in astro.config and applies it by rendering <Font> in a page head, often through a shared layout. That shared layout puts the font module into the dependency graph for every page that uses it.

Three independent reports, #17626, #17642, and #17652, traced the Astro 7.2 failure to the same generated virtual module. During prerendering, Astro starts a temporary font server on an operating-system-assigned port. The generated module contains that address, so its code differs on the next build even when the project does not.

Incremental builds hash a page's module dependency graph. A different port therefore produces a different dependencyHash. Astro rejects the cached page before its stable cacheKey can help. The HTML can be identical while the cache still misses.

The reproduced result

I ran the public minimal fixture with its pinned Astro 7.2.0 and local Roboto file. Two unchanged builds produced different hashes and restored no page:

{
  "hashesMatch": false,
  "secondBuildRestored": false
}

Then I removed only the <Font> component from the test page while leaving the font configuration in place. The same two-build test produced an identical dependency hash and restored the page:

{
  "hashesMatch": true,
  "secondBuildRestored": true
}

That control is useful for diagnosis, but removing <Font> is not automatically a production fix. The component adds the font declarations and preload hints to the page. Removing it without replacing that work can change typography or make the font disappear.

Choose the temporary workaround

Keep the Fonts API

If the site already depends on Astro's generated font declarations, optimized fallbacks, or preload behavior, disable incremental builds for now. Remove the experimental flag or set it to false, then build normally. This gives up page restoration but preserves the rendered font setup.

import { defineConfig } from "astro/config";

export default defineConfig({
  experimental: {
    incrementalBuild: false,
  },
  // Keep the existing fonts configuration.
});

Keep incremental builds

If build time is the stronger constraint, stop rendering <Font> and restore the font loading setup you used before the Fonts API. Treat that as a visual and performance change. Verify generated @font-face rules, preload links, network requests, fallback behavior, and layout shift in a production build.

Do not simply remove the component because the cache test turns green. A faster build that ships the wrong font is not a fix.

Test an upstream preview only on a branch

The verified fix attempts in pull request #17645 and pull request #17653 were still open on August 10, 2026. Their pkg.pr.new builds are useful for confirming the diagnosis, but they are not released Astro versions. Do not turn a temporary preview URL into an unattended production dependency.

The npm registry still listed Astro 7.2.0 as the current release during this test. Wait for a released version whose notes include the accepted fix, then upgrade through the normal package range and repeat the two-build check.

Do not misdiagnose the other cache failures

A different dependencyHash on two local builds with <Font> rendered matches this issue. These symptoms point elsewhere:

  • No cache manifest in CI: restore cacheDir before the build.
  • A stable dependencyHash but a changed cacheKey: fix the data version supplied by getStaticPaths().
  • A warning about build.concurrency: set it to 1 before testing incremental reuse.
  • A configHash or lockfileHash change: the whole cache is expected to become ineligible.

If the page is restored but an optimized image used by that page is missing, that is a separate Astro and Cloudflare image issue. Do not use the Fonts API workaround for missing image output.

A short rollout plan

  • Save one known eligible route and its cacheKey.
  • Run two unchanged builds and record the dependencyHash and restored status.
  • Temporarily remove <Font> in a branch and repeat the test.
  • Choose whether font behavior or incremental reuse matters more until a fix ships.
  • Run a production build plus visual, font-network, and page-output checks.
  • After a released Astro update, remove the workaround and repeat the same evidence-based test.

Keep the before and after hashes in the dependency update. That turns a silent build-cache miss into a result another reviewer can verify, and it prevents a future Astro upgrade from being accepted only because the build completed.