Vercel Logo

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 PathRouted ToApp Only Sees
/marketing/
/pricingmarketing/pricing
/docsdocs/docs
/docs/apidocs/docs/api
/appdashboard/app
/settingsdashboard/settings

The docs app will never receive a request for /pricing. The marketing app will never see /app/projects.

Fast Track

  1. Review the execution order diagram above
  2. Understand that each app only sees its own paths
  3. 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:

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:

  1. Request arrives at Vercel
  2. Microfrontends checks: does /documentation match any routing rules?
  3. No match for /docs/:path* (different path), so it goes to marketing (default)
  4. Marketing's redirect fires: /documentation/docs
  5. Browser makes new request for /docs
  6. Microfrontends routes /docs to the docs app
  7. 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
  1. Visit http://localhost:3000/documentation
  2. You should be redirected to /docs
  3. 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 LocationPath Routes ToDoes Redirect Run?
marketingmarketing✅ Yes
marketingdocs❌ No
docsdocs✅ Yes
docsmarketing❌ 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:

HeaderDescription
x-vercel-mfe-appWhich app handled the request
x-vercel-mfe-matched-pathWhich pattern matched
x-vercel-mfe-target-deployment-idDeployment that served it

Enable debug mode:

  1. Set cookie: VERCEL_MFE_DEBUG=1
  2. 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/docs redirect

The Pattern for Shared Logic

Since middleware is isolated per app, how do you share logic like authentication?

Options:

  1. Duplicate middleware - Copy the same logic to each app (simple but redundant)
  2. Shared package - Create @acme/auth with middleware helpers
  3. Server Components/Actions - Move auth checks to layout.tsx or 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.