#16366 d69f858 Thanks @matthewp! - Adds a new experimental.advancedRouting option that lets you take full control of Astro's request handling pipeline by creating a src/app.ts file in your project.
Today, Astro handles every incoming request through a fixed internal pipeline: trailing slash normalization, redirects, actions, middleware, page rendering, i18n, and so on. That pipeline works great for most sites, but as projects grow you often want to run your own logic between those steps β an auth check before rendering, a rate limiter before actions, custom logging around the whole stack. Advanced routing gives you that control.
When enabled, Astro looks for a src/app.ts file in your project. If it finds one, that file becomes the entrypoint for all server-rendered requests. You compose the pipeline yourself using the handlers Astro provides, and you can slot your own logic anywhere in the chain.
Enabling advanced routing
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
experimental: {
advancedRouting: true,
},
});
Two ways to build your pipeline
Astro ships two entrypoints for advanced routing: astro/fetch and astro/hono.
astro/fetch is a low-level, framework-free API built on the Web Fetch standard. You create a FetchState from the incoming request, then call handler functions in sequence. Each handler takes the state, does its work, and returns a Response (or undefined to pass through). This is the core primitive that everything else is built on:
// src/app.ts
import {
FetchState,
trailingSlash,
redirects,
actions,
middleware,
pages,
i18n,
} from 'astro/fetch';
export default {
async fetch(request: Request) {
const state = new FetchState(request);
// Early exits β these return a Response only when they apply.
const slash = trailingSlash(state);
if (slash) return slash;
const redirect = redirects(state);
if (redirect) return redirect;
const action = await actions(state);
if (action) return action;
// Middleware wraps page rendering; i18n post-processes the response.
const response = await middleware(state, () => pages(state));
return i18n(state, response);
},
};
astro/hono wraps the same handlers as Hono middleware, so you can mix Astro's pipeline with Hono's ecosystem of middleware (logger, CORS, JWT, rate limiting, etc.) using the app.use() pattern you already know:
// src/app.ts
import { Hono } from 'hono';
import { getCookie } from 'hono/cookie';
import { logger } from 'hono/logger';
import { actions, middleware, pages, i18n } from 'astro/hono';
const app = new Hono();
app.use(logger());
// Auth gate β only runs for /dashboard routes.
app.use('/dashboard/*', async (c, next) => {
const session = getCookie(c, 'session');
if (!session) return c.redirect('/login');
return next();
});
app.use(actions());
app.use(middleware());
app.use(pages());
app.use(i18n());
export default app;
Both approaches give you the same power β pick whichever fits your project. If you don't need a framework, astro/fetch keeps things minimal. If you want a rich middleware ecosystem, astro/hono gets you there with one import.
For more information on enabling and using this feature in your project, see the experimental advanced routing docs. To give feedback, or to keep up with its development, see the advanced routing RFC for more information and discussion.
#16519 1b1c218 Thanks @louisescher! - Adds support for redirecting URLs in remote image optimization.
Previously, when a remote image URL meant to be optimized by Astro led to a redirect, Astro would fail silently and ignore the redirect. Now, Astro tracks up to 10 redirects for these images. If any of the redirects are not covered by a pattern in image.remotePatterns or a domain in image.domains, Astro will fail with a helpful error message.
In the following example, the first image would be loaded successfully, while the second would lead to Astro throwing an error:
export default defineConfig({
image: {
domains: ['example.com', 'cdn.example.com'],
},
});
{
/* Redirects to https://cdn.example.com/assets/image.png: */
}
<Image
src="https://example.com/assets/image.png"
width="1920"
height="1080"
alt="An example image."
/>;
{
/* Redirects to https://malicious.com/image.png: */
}
<Image
src="https://example.com/bad-image.png"
width="1920"
height="1080"
alt="An example image."
/>;
In cases where all redirects to HTTPS hosts should be trusted, the following configuration for image.remotePatterns can be used:
export default defineConfig({
image: {
remotePatterns: [
{
protocol: 'https',
},
],
},
});