Routing Middleware now supports Python
By Steven Van ·
Python apps on Vercel, including FastAPI, Flask and Django, can now run middleware written in Python through the vercel-proxy package.
Python apps on Vercel can now use Routing Middleware written in Python, as the Python reference describes. It works with apps on the Python runtime, such as FastAPI, Flask and Django. The middleware runs before requests reach the app and can continue, rewrite, redirect or answer them.
Setup
The middleware is an ASGI or WSGI app that runs separately from the main app. It has access only to the packages in the vercel.proxy dependency group of pyproject.toml. Add vercel-proxy to that group, along with any other package the middleware imports. If one is missing, requests to the middleware fail. Projects that only use requirements.txt need to switch to pyproject.toml.
- Add the package with uv add --group vercel.proxy vercel-proxy.
- Set proxy.entrypoint in vercel.json to module:attribute, for example proxy:proxy. A plain file path such as proxy.py is rejected.
- The entrypoint can also name another ASGI or WSGI app instead of a Proxy.
The release note shows the entrypoint in vercel.toml, but the reference page uses vercel.json.
Routing with Proxy
The vercel-proxy package provides a Proxy app. Handlers are registered with @proxy.route(path), can be sync or async, and the first matching route handles each request. Paths can capture values with placeholders such as {slug}, {user_id:int} or {path:path}, which handlers read from request.path_params. Routes can also be limited to certain HTTP methods or a host, and hosts can capture values too, such as {tenant}.example.com.
Trailing slashes are optional by default, and strict=True requires exact matches. Requests that match no route continue to the app unchanged, unless a fallback response or handler is set. Captured values are URL-decoded, so the docs advise quoting them before building a URL from them.
Responses
- ContinueResponse() sends the request on to the original URL.
- RewriteResponse(url) serves the request from another URL without changing what the client sees.
- ContinueResponse and RewriteResponse accept request_headers to set headers on the forwarded request. A None value removes a header.
- Response, JSONResponse, PlainTextResponse, HTMLResponse and RedirectResponse answer the request directly, so it never reaches the app. RedirectResponse defaults to status 307.
Headers and cookies can be set on any response. Header names starting with x-middleware- are reserved, and Starlette background tasks are not supported. Duration, memory and other options are set through the functions property in vercel.json, keyed by the module's file path, such as app/proxy.py.