Type something to search...
What is route priority in Astro and how does it resolve conflicts?

What is route priority in Astro and how does it resolve conflicts?

Route priority is the set of rules Astro uses to decide which file handles a URL when more than one route could match it. In short, more specific routes win: Astro's reserved routes come first, then routes with more path segments, then static routes over dynamic ones, named parameters over rest parameters, prerendered dynamic routes over on-demand ones, and endpoints over pages. If the rules still can't separate two routes, Astro sorts them alphabetically. That's why src/pages/posts/create.astro always serves /posts/create, even if posts/[id].astro and a root [...slug].astro could match it too.

You rarely think about priority until a page shows the wrong content or a build prints a warning about a conflict. In this article you'll learn each rule in order, see them applied to a real file tree with the actual output from a test server, understand what happens with prerendered paths that collide, find out which collisions Astro warns about (and which will become hard errors), and see how redirects fit in, including one behaviour in Astro 7.4.2 that differs from the docs.

Why Routes Conflict

Astro's file-based routing lets you mix static files, dynamic parameters and rest parameters in the same folder. That flexibility means a URL like /posts/create can match several files at once:

src/pages/
  [...slug].astro
  posts/
    create.astro
    [page].astro
    [pid].ts
    [...slug].astro

Every one of those five files could, in principle, produce /posts/create. Astro needs a deterministic way to choose, and route priority is that mechanism.

The Rules, in Order

Astro sorts routes using these rules, applying each one only when the previous rules didn't decide:

OrderRuleExample winner
1Reserved routes first/_astro/... assets
2More path segments beat fewerposts/[...slug] over root [...slug]
3Static beats dynamicposts/create over posts/[page]
4Named parameters beat rest parametersposts/[page] over posts/[...slug]
5Prerendered dynamic beats on-demand dynamicA getStaticPaths() page over a prerender = false one
6Endpoints beat pagesposts/[pid].ts over a page with the same pattern
7File-based routes beat redirects(see the redirects section below)
8Alphabetical, using your Node default localeLast resort

Rule 1: Reserved Routes

Some paths are used internally and always take priority over your files and over integrations:

  • /_astro/: built CSS, client scripts, optimised images and other Vite assets.
  • /_server_islands/: serves components deferred as server islands.
  • /_actions/: serves your Astro actions.

Don't create pages under these paths. They will never be reached.

Rule 2: More Segments Win

A route with more path segments is more specific. Everything under src/pages/posts/ beats the root [...slug].astro for URLs that start with /posts/, even though the root catch-all could technically match them.

Rule 3: Static Beats Dynamic

A route with no parameters wins over any route with parameters in the same position. posts/create.astro takes /posts/create from all the dynamic siblings.

Rule 4: Named Beats Rest

A named parameter matches exactly one segment, so it's more specific than a rest parameter, which can match any number. posts/[page].astro wins over posts/[...slug].astro for single-segment URLs such as /posts/2.

Rule 5: Prerendered Beats On-Demand

When two dynamic routes have the same shape, the prerendered one, with its known list of paths from getStaticPaths(), takes priority over the one rendered on demand. The on-demand route then handles everything the prerendered one didn't build. This is a useful pattern, as the test below shows.

Rule 6: Endpoints Beat Pages

If an endpoint and a page share a pattern, the endpoint wins.

Testing the Rules on a Real Tree

I built the tree from the start of this article with the Node adapter. Each page printed its own name and Astro.params, and the endpoint did the same as plain text. To show rule 5, posts/[page].astro was prerendered with two paths:

---
// src/pages/posts/[page].astro (prerendered)
export function getStaticPaths() {
  return [{ params: { page: "1" } }, { params: { page: "2" } }];
}
---
<p>posts/[page] {JSON.stringify(Astro.params)}</p>

The endpoint and the other pages were on demand:

// src/pages/posts/[pid].ts
export const prerender = false;

export const GET = ({ params }) =>
  new Response(`endpoint posts/[pid].ts ${JSON.stringify(params)}`);

Requests against the built server returned:

/posts/create -> posts/create {}
/posts/1      -> posts/[page] {"page":"1"}
/posts/2      -> posts/[page] {"page":"2"}
/posts/3      -> endpoint posts/[pid].ts {"pid":"3"}
/posts/abc    -> endpoint posts/[pid].ts {"pid":"abc"}
/posts/1/2    -> posts/[...slug] {"slug":"1/2"}
/abc          -> root [...slug] {"slug":"abc"}

Reading the results line by line:

  1. /posts/create: the static file wins (rule 3).
  2. /posts/1 and /posts/2: both [page] and [pid] have a single named parameter, but [page] is prerendered and built these two paths, so it wins (rule 5).
  3. /posts/3 and /posts/abc: the prerendered route didn't build these, so the on-demand endpoint handles them.
  4. /posts/1/2: two segments, so only a rest parameter can match. posts/[...slug] beats the root catch-all (rule 2).
  5. /abc: nothing under /posts/ applies, so the root catch-all takes it.

The combination in points 2 and 3 is handy in practice: prerender your known pages for speed, and let an on-demand route with the same shape handle everything else.

Collisions Astro Warns About

Some combinations can't be resolved sensibly, and Astro warns about them at build time. The warnings also say they will become hard errors in future versions, so treat them as errors now.

Two On-Demand Routes with the Same Shape

In my first version of the test, both posts/[page].astro and posts/[pid].ts were on demand. The build printed:

[WARN] [router] The route "/posts/[pid]" is defined in both "src/pages/posts/[pid].ts" and "src/pages/posts/[page].astro" using SSR mode. A dynamic SSR route cannot be defined more than once.
[WARN] [router] A collision will result in a hard error in following versions of Astro.

Requests like /posts/1 went to the endpoint, consistent with rule 6, but you shouldn't rely on that. Give each route a distinct shape, such as posts/page/[page].astro and api/posts/[pid].ts.

Two Files for the Same Static URL

about.astro and about/index.astro both produce /about. With both present, the build warned:

[WARN] [router] The route "/about" is defined in both "src/pages/about/index.astro" and "src/pages/about.astro". A static route cannot be defined more than once.
[WARN] [router] A collision will result in a hard error in following versions of Astro.
[WARN] [build] Could not render `/about` from route `/about` as it conflicts with higher priority route `/about`.

Only one page was built. Keep one of the two files.

Collisions Between Prerendered Paths

In static builds, conflicts often come from getStaticPaths(): two dynamic routes return the same path, or a dynamic route returns a path that a static file already owns. I tested this in a tags/ folder:

src/pages/tags/
  featured.astro      -> static
  [tag].astro         -> returns "astro" and "featured"
  [...path].astro     -> returns "featured" and "css/2"

The build skipped the lower-priority duplicates and said so:

[WARN] [build] Could not render `/tags/featured` from route `/tags/[tag]` as it conflicts with higher priority route `/tags/featured`.
[WARN] [build] Could not render `/tags/featured` from route `/tags/[...path]` as it conflicts with higher priority route `/tags/featured`.

The output contained the static page at /tags/featured/, [tag] built /tags/astro/, and [...path] built /tags/css/2/. After deleting featured.astro, the conflict moved down a level:

[WARN] [build] Could not render `/tags/featured` from route `/tags/[...path]` as it conflicts with higher priority route `/tags/[tag]`.

and /tags/featured/ was rendered by [tag].astro, as rule 4 predicts. The build still succeeds in these cases, so scan your build output for Could not render warnings. They usually mean a getStaticPaths() function is returning more than it should, often because a tag or slug happens to match a real page name.

Redirects and Priority

Configured redirects in astro.config.mjs take part in the same sorting. The docs say file-based routes take precedence over redirects, so a redirect from /old-page shouldn't fire if src/pages/old-page.astro exists.

When I tested this on Astro 7.4.2 with a redirect "/clash": "/about" and a real src/pages/clash.astro, the redirect won everywhere: the static build wrote redirect HTML to dist/clash/index.html, and both the dev server and the Node adapter answered /clash with a 301. Whatever the intended behaviour, don't define a redirect and a page on the same path. The details are in how to set up redirects in Astro.

Route Priority and Advanced Routing in Astro 7

Astro 7 turns on advanced routing by default, which lets a src/fetch.ts file control the request pipeline. It doesn't change route priority. The FetchState object resolves the matched route using the same rules, and you can read the result from state.routeData. What you can change is which stages run, and in what order, around that matching. If you add custom logic before Astro's handlers, remember that the docs warn the raw request pathname may differ from the route Astro matches, so don't make security decisions by comparing pathname strings.

Practical Tips

  • Prefer distinct shapes: blog/[slug].astro and blog/page/[page].astro never compete, while blog/[slug].astro and blog/[page].astro always do.
  • Use static files for special cases: a posts/create.astro page is the cleanest way to carve one URL out of a dynamic route.
  • Keep catch-alls deep: a root [...slug].astro captures every unmatched URL on the site. A catch-all under /docs/ only affects docs.
  • Read the build log: Could not render and collision warnings point straight at overlapping routes.
  • Combine prerendered and on-demand routes deliberately: rule 5 lets a prerendered route serve known pages while an on-demand route of the same shape handles the rest.

FAQ: Route Priority in Astro

The static page. A route without parameters always takes priority over a route with parameters at the same position, so posts/create.astro beats posts/[id].astro for /posts/create.

Yes. A route like posts/[page].astro is more specific than posts/[...slug].astro, so it handles single-segment URLs and the rest parameter only gets URLs it can't match.

Astro builds the path from the higher-priority route and skips the other, printing a warning that it could not render the path because it conflicts with a higher priority route. The build still completes.

No. Astro 7.4.2 warns that a dynamic SSR route cannot be defined more than once and that this will become a hard error. Give the routes different shapes, or prerender one of them.

The docs say pages take precedence over redirects, but in my Astro 7.4.2 tests the redirect won in the static build, the dev server and the Node adapter. Avoid defining both for one path.

No. The fetch entrypoint controls the order of pipeline stages such as redirects, middleware and rendering, but route matching still follows the same priority rules.


Conclusion

Route priority is Astro's way of choosing the most specific route for each URL. Reserved routes come first, then deeper routes, static over dynamic, named over rest parameters, prerendered over on-demand, endpoints over pages, and alphabetical order as the final tiebreaker. Once you know the order, it's easy to predict which file will handle a request, and to use the rules on purpose, for example carving out a static page from a dynamic route.

When routes do overlap, Astro tells you: collision warnings for duplicate patterns, and Could not render warnings for duplicate prerendered paths. Treat those warnings as errors, keep route shapes distinct, and test redirects against real pages, and your routing will stay predictable as the site grows.

Tags :
Share :

Related Posts

What are Astro Actions and how do they work?

What are Astro Actions and how do they work?

Astro Actions are type-safe backend functions that you define once on the server and then call from the browser as if they were ordinary async fu

Dive Deeper
What are Astro adapters and which one should you choose?

What are Astro adapters and which one should you choose?

An Astro adapter is an integration that builds the server part of your project for a specific runtime, such as Node.js, Vercel Functions, Netlify

Dive Deeper
How to build API endpoints in Astro?

How to build API endpoints in Astro?

To build an API endpoint in Astro, create a .js or .ts file in src/pages/ and export a function named after an HTTP method, such as GET or

Dive Deeper