Getting Started with Routing Middleware

Getting Started with Routing Middleware

Routing Middleware lets you to run code before your pages load, giving you control over incoming requests. It runs close to your users for fast response times and are perfect for redirects, authentication, and request modification.

Getting Started Steps

  1. Make sure the Vercel CLI is installed (npm i -g vercel). If using Claude Code or Cursor, install the Vercel Plugin (npx plugins add vercel/vercel-plugin). For other agents, install Vercel Skills (npx skills add vercel-labs/agent-skills).
  2. Create a middleware file (middleware.ts at the project root, or proxy.ts if using Next.js 16+).
  3. Add a redirect from /old-blog to /blog using a permanent redirect.
  4. Configure the matcher to run on all paths except static files and images.
  5. Test locally with vercel dev, then deploy with vercel --prod.

Routing Middleware is available on the Node.js, Bun, and Edge runtimes. Edge is the default runtime for Routing Middleware. To use Node.js, configure the runtime in your middleware config. To use Bun, set bunVersion in your vercel.json file.

Next.js 16 users: Next.js 16 renamed the middleware file from middleware.ts to proxy.ts and changed the function export from middleware to proxy. When using Next.js 16 or later, use proxy.ts instead of middleware.ts. The proxy function runs on Node.js only (Edge runtime is not supported). See the Next.js proxy documentation for details.

What you will learn

Prerequisites

Creating a Routing Middleware

Create a new file for your Routing Middleware

Create a file called middleware.ts in your project root (same level as your package.json) and add the following code:

export const config = {
     runtime: 'nodejs', // optional: use 'nodejs' or omit for 'edge' (default)
};

export default function middleware(request: Request) {
     console.log('Request to:', request.url);
     return new Response('Logging request URL from Middleware');
}

Deploy your project and visit any page. You should see "Logging request URL from Middleware" instead of your normal page content.

Redirecting users

To redirect users based on their URL, add a new route to your project called /blog, and modify your middleware.ts to include a redirect condition.

export const config = {
     runtime: 'nodejs', // optional: use 'nodejs' or omit for 'edge' (default)
};

export default function middleware(request: Request) {
     const url = new URL(request.url);

// Redirect old blog path to new one
     if (url.pathname === '/old-blog') {
       return new Response(null, {
         status: 302,
         headers: { Location: '/blog' },
       });
     }

// Let other requests continue normally
     return new Response('Other pages work normally');
}

Try visiting /old-blog - you should be redirected to /blog.

Configure which paths trigger the middleware

By default, Routing Middleware runs on every request. To limit it to specific paths, you can use the config object:

export default function middleware(request: Request) {
     const url = new URL(request.url);

// Only handle specific redirects
     if (url.pathname === '/old-blog') {
       return new Response(null, {
         status: 302,
         headers: { Location: '/blog' },
       });
     }

return new Response('Middleware processed this request');
}

// Configure which paths trigger the Middleware
export const config = {
     matcher: [
       // Run on all paths except static files
       '/((?!_next/static|_next/image|favicon.ico).*)',
       // Or be more specific:
       // '/blog/:path*',
       // '/api/:path*'
     ],
};

Debugging Routing Middleware

When things don't work as expected:

  1. Check the logs: Use console.log() liberally and check your Vercel dashboard Logs section.
  2. Test the matcher: Make sure your paths are actually triggering the Routing Middleware.
  3. Verify headers: Log request.headers to see what's available.
  4. Test locally: Routing Middleware works in development too so you can debug before deploying.
export default function middleware(request: Request) {
  // Debug logging
  console.log('URL:', request.url);
  console.log('Method:', request.method);
  console.log('Headers:', Object.fromEntries(request.headers.entries()));

// Your middleware logic here...
}

Middleware reference

Detail Value
File location Any path set with proxy.entrypoint, or middleware.ts in project root (proxy.ts for Next.js 16+)
Export export default function middleware(request: Request) (or export function proxy for Next.js 16+)
Config export export const config = { matcher: [...] }
Default runtime edge (set runtime: 'nodejs' in config for Node.js)
Bun runtime Set bunVersion in vercel.json and runtime: 'nodejs' in config
Request object Standard Request API
Geo headers x-vercel-ip-country, x-vercel-ip-country-region, x-vercel-ip-city
Path matching Supports regex, named params, and wildcards in the matcher config

Next steps