Skip to main content
Zhimalab
中文

Astro: Conflict Between the public Directory and Page Routes

2026-07-09 · 11 min

Problem description

On zhimalab.tech, the Math Knowledge Map page rendered incorrectly in production:

  • The page showed only the knowledge map itself (knowledge cards on a dark background) — missing the site’s navigation bar, breadcrumb and footer
  • With pnpm run dev locally everything was fine; the page had the full navigation and layout

Investigation

1. Page structure analysis

The page has two layers:

  • The Astro page src/pages/tutorial/math.astro — uses the BaseLayout with navigation bar and breadcrumb, embedding the static content via an iframe
  • Static HTML files in the public/tutorial/math/ directory — containing index.html (the knowledge map home page) plus 95+ knowledge-point pages

The Astro page code roughly looks like:

---
import BaseLayout from "../../layouts/BaseLayout.astro";
---

<BaseLayout title="小学数学" ... layoutType="tool-detail">
  <div class="w-full h-[calc(100vh-3.5rem)]">
    <iframe src="/tutorial/math/index.html" ...></iframe>
  </div>
</BaseLayout>

2. Build output comparison

After astro build, the dist/ directory structure looks like:

dist/
  tutorial/
    math.html          ← Astro page build output
    math/
      index.html       ← static file from public/
      1上-数一数比多少.html
      ...

Astro builds src/pages/tutorial/math.astro into dist/tutorial/math.html, and also copies public/tutorial/math/index.html to dist/tutorial/math/index.html.

3. Behavioral difference in production

In local dev (pnpm run dev), the Astro dev server handles routing correctly:

  • Visiting /tutorial/math → renders the math.astro page
  • Visiting /tutorial/math/index.html → serves the static file

But in the Cloudflare Pages production environment, when you visit /tutorial/math/, the server sees the directory dist/tutorial/math/, and since that directory has an index.html, it directly returns the static file, completely bypassing the Astro page. That’s why only the knowledge-map content showed, without the site’s navigation layout.

The solution

Change the Astro page’s route path to one that doesn’t conflict with the static directory:

Steps

  1. Rename src/pages/tutorial/math.astro to src/pages/tutorial/math-map.astro
  2. Update all links pointing to /tutorial/math to /tutorial/math-map
  3. Leave the iframe’s src="/tutorial/math/index.html" unchanged (it still points to the static file)

After the change:

src/pages/tutorial/
  math.astro       →   math-map.astro    (renamed)
  index.astro      ←   link changed to /tutorial/math-map
src/pages/
  index.astro      ←   link changed to /tutorial/math-map

Why does this fix it?

After renaming, the build output becomes:

dist/
  tutorial/
    math-map/
      index.html       ← Astro page (navigation layout + iframe)
    math/
      index.html       ← static knowledge map content

Now visiting /tutorial/math-map/ serves the Astro page, and visiting /tutorial/math/ serves the static knowledge map. The two no longer conflict.

Takeaways

Astro’s public/ directory vs routes

This is a common pitfall in Astro projects. Files in public/ are copied directly to the same path in dist/ after building, and Astro’s page routes also generate HTML files in dist/. When the two paths overlap, static servers (like Cloudflare Pages, Nginx) prefer the index.html in the directory over a same-named single file.

Preventive measures

  • Avoid creating directories in public/ with the same name as src/pages/ routes
  • If you need to store many static files in public/, consider putting them under a distinctive subpath (e.g. public/static/)
  • Or keep a clear distinction between file names in public/ and page route names

Dev vs production differences

Problems like this are hard to spot in local development because the Astro dev server has smarter routing logic. Always preview in production mode with astro build && astro preview to catch these issues early.