plugins/vercel/skills/routing-middleware/SKILL.md
Vercel Routing Middleware guidance — request interception before cache, rewrites, redirects, personalization. Works with any framework. Supports Edge, Node.js, and Bun runtimes. Use when intercepting requests at the platform level.
npx skillsauth add openai/plugins routing-middlewareInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert in Vercel Routing Middleware — the platform-level request interception layer.
Routing Middleware runs before the cache on every request matching its config. It is a Vercel platform feature (not framework-specific) that works with Next.js, SvelteKit, Astro, Nuxt, or any deployed framework. Built on Fluid Compute.
middleware.ts or middleware.js at the project rootruntime: 'nodejs'), Bun (Node.js + bunVersion in vercel.json)There are THREE "middleware" concepts in the Vercel ecosystem:
| Concept | File | Runtime | Scope | When to Use |
|---------|------|---------|-------|-------------|
| Vercel Routing Middleware | middleware.ts (root) | Edge/Node/Bun | Any framework, platform-level | Request interception before cache: rewrites, redirects, geo, A/B |
| Next.js 16 Proxy | proxy.ts (root, or src/proxy.ts if using --src-dir) | Node.js only | Next.js 16+ only | Network-boundary proxy needing full Node APIs. NOT for auth. |
| Edge Functions | Any function file | V8 isolates | General-purpose | Standalone edge compute endpoints, not an interception layer |
Why the rename in Next.js 16: middleware.ts → proxy.ts clarifies it sits at the network boundary (not general-purpose middleware). Partly motivated by CVE-2025-29927 (middleware auth bypass via x-middleware-subrequest header). The exported function must also be renamed from middleware to proxy. Migration codemod: npx @next/codemod@latest middleware-to-proxy
Deprecation: Next.js 16 still accepts middleware.ts but treats it as deprecated and logs a warning. It will be removed in a future version.
To run Routing Middleware (and all Vercel Functions) on Bun, add bunVersion to vercel.json:
{
"bunVersion": "1.x"
}
Set the middleware runtime to nodejs — Bun replaces the Node.js runtime transparently:
export const config = {
runtime: 'nodejs', // Bun swaps in when bunVersion is set
};
Bun reduces average latency by ~28% in CPU-bound workloads. Currently in Public Beta — supports Next.js, Express, Hono, and Nitro.
// middleware.ts (project root)
import { geolocation, rewrite } from '@vercel/functions';
export default function middleware(request: Request) {
const { country } = geolocation(request);
const url = new URL(request.url);
url.pathname = country === 'US' ? '/us' + url.pathname : '/intl' + url.pathname;
return rewrite(url);
}
export const config = {
runtime: 'edge', // 'edge' (default) | 'nodejs'
};
@vercel/functions)For non-Next.js frameworks, import from @vercel/functions:
| Helper | Purpose |
|--------|---------|
| next() | Continue middleware chain (optionally modify headers) |
| rewrite(url) | Transparently serve content from a different URL |
| geolocation(request) | Get city, country, latitude, longitude, region |
| ipAddress(request) | Get client IP address |
| waitUntil(promise) | Keep function running after response is sent |
For Next.js, equivalent helpers are on NextResponse (next(), rewrite(), redirect()) and NextRequest (request.geo, request.ip).
Middleware runs on every route by default. Use config.matcher to scope it:
// Single path
export const config = { matcher: '/dashboard/:path*' };
// Multiple paths
export const config = { matcher: ['/dashboard/:path*', '/api/:path*'] };
// Regex: exclude static files
export const config = {
matcher: ['/((?!_next/static|favicon.ico).*)'],
};
Tip: Using matcher is preferred — unmatched paths skip middleware invocation entirely (saves compute).
import { ipAddress, next } from '@vercel/functions';
export default function middleware(request: Request) {
return next({ headers: { 'x-real-ip': ipAddress(request) || 'unknown' } });
}
import { get } from '@vercel/edge-config';
import { rewrite } from '@vercel/functions';
export default async function middleware(request: Request) {
const variant = await get('experiment-homepage'); // <1ms read
const url = new URL(request.url);
url.pathname = variant === 'B' ? '/home-b' : '/home-a';
return rewrite(url);
}
import type { RequestContext } from '@vercel/functions';
export default function middleware(request: Request, context: RequestContext) {
context.waitUntil(
fetch('https://analytics.example.com/log', { method: 'POST', body: request.url })
);
return new Response('OK');
}
| Limit | Value | |-------|-------| | Max URL length | 14 KB | | Max request body | 4 MB | | Max request headers | 64 headers / 16 KB total |
Vercel's CDN supports three routing mechanisms, evaluated in this order:
| Order | Mechanism | Scope | Deploy Required | How to Configure |
|-------|-----------|-------|-----------------|------------------|
| 1 | Bulk Redirects | Up to 1M static path→path redirects | No (runtime via Dashboard/API/CLI) | Dashboard, CSV upload, REST API |
| 2 | Project-Level Routes | Headers, rewrites, redirects | No (instant publish) | Dashboard, API, CLI, Vercel SDK |
| 3 | Deployment Config Routes | Full routing rules | Yes (deploy) | vercel.json, vercel.ts, next.config.ts |
Project-level routes (added March 2026) let you update routing rules — response headers, rewrites to external APIs — without triggering a new deployment. They run after bulk redirects and before deployment config routes. Available on all plans.
Project-level routes take effect instantly (no deploy required). Four ways to manage them:
| Method | How |
|--------|-----|
| Dashboard | Project → CDN → Routing tab. Live map of global traffic, cache management, and route editor in one view. |
| REST API | GET/POST/PATCH/DELETE /v1/projects/{projectId}/routes — 8 dedicated endpoints for CRUD on project routes. |
| Vercel CLI | Managed via vercel.ts / @vercel/config commands (compile, validate, generate). |
| Vercel SDK | @vercel/config helpers: routes.redirect(), routes.rewrite(), routes.header(), plus has/missing conditions and transforms. |
Use project-level routes for operational changes (CORS headers, API proxy rewrites, A/B redirects) that shouldn't require a full redeploy.
vercel.tsInstead of static vercel.json, you can use vercel.ts (or .js, .mjs, .cjs, .mts) with the @vercel/config package for type-safe, dynamic routing configuration:
// vercel.ts
import { defineConfig } from '@vercel/config';
export default defineConfig({
rewrites: [
{ source: '/api/:path*', destination: 'https://backend.example.com/:path*' },
],
headers: [
{ source: '/(.*)', headers: [{ key: 'X-Frame-Options', value: 'DENY' }] },
],
});
CLI commands:
npx @vercel/config compile — compile to JSON (stdout)npx @vercel/config validate — validate and show summarynpx @vercel/config generate — generate vercel.json locally for developmentConstraint: Only one config file per project — vercel.json or vercel.ts, not both.
proxy.tstools
Top-level workflow skill for USD performance diagnosis and optimization. Use for slow loading, high memory, low FPS, or 'optimize my scene' requests; delegates auth/runtime setup to Phase 0 owners.
data-ai
Use when the user mentions MagicPath, designs, UI components, themes, canvas selections, or repo-to-canvas UI work; run magicpath-ai to search, inspect, install, or author components.
documentation
Use as the top-level router for Omniverse Realtime Viewer USD app requests and focused viewer reference documents.
tools
Turn Notion specs into implementation plans, tasks, and progress tracking; use when implementing PRDs/feature specs and creating Notion plans + tasks from them.