Path Routing Deep Dive
Microfrontends routing happens before your application code runs. This changes everything about how middleware, redirects, and rewrites behave.
Outcome
Understand the routing execution order and how path isolation affects middleware, redirects, and rewrites.
The Execution Order
When a request hits Vercel, it passes through these layers in order:
Request: GET /docs/api/overview
1. Firewall → Platform-wide rules (DDoS, rate limiting)
2. MICROFRONTENDS → Routes to correct project (/docs/* → docs app)
3. Headers → Response headers for docs app
4. Redirects → Redirect rules in docs app
5. Middleware → Docs app middleware only
6. Rewrites → Rewrite rules in docs app
7. Route Handler → Actual page renders
The key insight: Microfrontends routing happens at step 2. By the time your middleware runs (step 5), the request has already been routed to a specific application.
The Middleware Shift
This is the biggest conceptual change. Before microfrontends:
// middleware.ts in monolith
export function middleware(request: NextRequest) {
// This ran for EVERY request
if (request.nextUrl.pathname.startsWith("/app")) {
// Check auth
}
}After microfrontends:
// middleware.ts in docs app
export function middleware(request: NextRequest) {
// This ONLY runs for /docs/* requests
// Requests to /app/* never reach this middleware
}If you're used to a single central middleware for auth or logging, that pattern breaks. Each app's middleware only sees requests routed to that app.
Path Isolation Explained
After microfrontends routing, each app is isolated:
| Request Path | Routed To | App Only Sees |
|---|---|---|
/ | marketing | / |
/pricing | marketing | /pricing |
/docs | docs | /docs |
/docs/api | docs | /docs/api |
/app | dashboard | /app |
/settings | dashboard | /settings |
The docs app will never receive a request for /pricing. The marketing app will never see /app/projects.
Fast Track
- Review the execution order diagram above
- Understand that each app only sees its own paths
- Add a redirect to test path routing behavior
Hands-on Exercise 2.2
Test path isolation by adding a redirect that crosses app boundaries.
Part 1: Add a Cross-App Redirect
Add a redirect to apps/marketing/next.config.ts:
import type { NextConfig } from "next";
import { withMicrofrontends } from "@vercel/microfrontends/next/config";
const nextConfig: NextConfig = {
redirects: async () => [
{
source: "/documentation",
destination: "/docs",
permanent: true,
},
],
};
export default withMicrofrontends(nextConfig);Part 2: Understand What Happens
When you visit /documentation:
- Request arrives at Vercel
- Microfrontends checks: does
/documentationmatch any routing rules? - No match for
/docs/:path*(different path), so it goes to marketing (default) - Marketing's redirect fires:
/documentation→/docs - Browser makes new request for
/docs - Microfrontends routes
/docsto the docs app - Docs app serves the page
Key insight: The redirect works because /documentation routes to marketing first, where the redirect is defined.
Part 3: What Would Break
If you added /documentation to the docs app's routing:
"@acme/docs": {
"routing": [
{ "paths": ["/docs", "/docs/:path*", "/documentation"] }
]
}Now the redirect in marketing would never run because /documentation goes directly to docs. The redirect lives in marketing, but the path routes to docs.
Try It
Start the apps and test the redirect:
pnpm dev- Visit http://localhost:3000/documentation
- You should be redirected to
/docs - The docs app serves the page
Why Redirects Can "Disappear"
A common frustration: "I added a redirect, but it doesn't work!"
The rule: Redirects only run in the app that receives the request.
| Redirect Location | Path Routes To | Does Redirect Run? |
|---|---|---|
| marketing | marketing | ✅ Yes |
| marketing | docs | ❌ No |
| docs | docs | ✅ Yes |
| docs | marketing | ❌ No |
If your redirect isn't working, check which app the path routes to.
Debug Headers
In production, enable debug headers to see routing decisions:
| Header | Description |
|---|---|
x-vercel-mfe-app | Which app handled the request |
x-vercel-mfe-matched-path | Which pattern matched |
x-vercel-mfe-target-deployment-id | Deployment that served it |
Enable debug mode:
- Set cookie:
VERCEL_MFE_DEBUG=1 - Or use Vercel Toolbar → Microfrontends panel → Enable Debug Mode
We'll use these in Lesson 3.3 (Observability).
Commit
git add -A
git commit -m "feat: add redirect to demonstrate path routing"Done-When
- You can explain the execution order: Firewall → MFE → Headers → Redirects → Middleware → Rewrites
- You understand that each app only sees requests for its own paths
- You know why redirects can "disappear" (defined in wrong app)
- You've tested the
/documentation→/docsredirect
The Pattern for Shared Logic
Since middleware is isolated per app, how do you share logic like authentication?
Options:
- Duplicate middleware - Copy the same logic to each app (simple but redundant)
- Shared package - Create
@acme/authwith middleware helpers - Server Components/Actions - Move auth checks to
layout.tsxor Server Actions (recommended in Next.js 16)
We'll implement the shared package approach in Lesson 2.4.
What's Next
Enable the local proxy and see all three apps stitched together at localhost:3024.
Was this helpful?