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.
Help me set up Routing Middleware in this project. First, make sure the Vercel CLI is installed (`npm i -g vercel`). If I'm 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`). Then: 1. Create a middleware file (middleware.ts at the project root, or proxy.ts if using Next.js 16+). 2. Add a redirect from /old-blog to /blog. 3. Configure the matcher to run on all paths except static files and images. 4. Test locally with `vercel dev`, then deploy with `vercel --prod`.
Routing Middleware is available on the Node.js and Bun runtimes. Node.js is the default runtime for Routing Middleware. To use Bun, set bunVersion in your vercel.json file.
- 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
- A Vercel project
- Basic knowledge of JavaScript/TypeScript
The following steps will guide you through creating your first Routing Middleware.
Create a file called
middleware.tsin your project root (same level as yourpackage.json) and add the following code:middleware.tsexport const config = { runtime: 'nodejs', }; 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 tonodejs. 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.
To redirect users based on their URL, add a new route to your project called
/blog, and modify yourmiddleware.tsto include a redirect condition.middleware.tsimport { next } from '@vercel/functions'; export const config = { runtime: 'nodejs', }; 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 next(); }- 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 - For every other path,
next()from the@vercel/functionspackage lets the request continue to your page
Try visiting
/old-blog- you should be redirected to/blog.- You use
By default, Routing Middleware runs on every request. To limit it to specific paths, you can use the
configobject:middleware.tsimport { next } from '@vercel/functions'; 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' }, }); } // Let other requests continue normally return next(); } // 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
See the API Reference for more details on the
configobject and matcher patterns.- The
When things don't work as expected:
- Check the logs: Use
console.log()liberally and check your Vercel dashboard Logs section in the sidebar - 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
middleware.tsexport 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... }- Check the logs: Use
| 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 | nodejs |
| 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 |
Was this helpful?