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
- 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). - Create a middleware file (
middleware.tsat the project root, orproxy.tsif using Next.js 16+). - Add a redirect from
/old-blogto/blogusing a permanent redirect. - Configure the matcher to run on all paths except static files and images.
- Test locally with
vercel dev, then deploy withvercel --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
- Create your first Routing Middleware
- Redirect users based on URLs
- Add conditional logic to handle different scenarios
- Configure which paths your Routing Middleware runs on
Prerequisites
- A Vercel project
- Basic knowledge of JavaScript/TypeScript
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');
}
- Every request to your site will trigger this function.
- You log the request URL to see what's being accessed.
- You return a response to prove the middleware is running.
- The
runtimeconfig is optional and defaults toedge. To use Bun, setbunVersioninvercel.jsoninstead.
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');
}
- You use
new URL(request.url)to parse the incoming URL. - You check if the path matches
/old-blog. - If it does, you return a redirect response (status 302).
- The
Locationheader tells the browser where to go.
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*'
],
};
- The
matcherarray defines which paths trigger your Routing Middleware. - The regex excludes static files (images, CSS, etc.) for better performance.
- You can also use simple patterns like
/blog/:path*for specific sections.
Debugging Routing Middleware
When things don't work as expected:
- Check the logs: Use
console.log()liberally and check your Vercel dashboard Logs section. - Test the matcher: Make sure your paths are actually triggering the Routing Middleware.
- Verify headers: Log
request.headersto see what's available. - 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
- Routing Middleware overview
- Routing Middleware API reference
Last updated August 14, 2026