Vercel FastAPI Cuts Function Use for Static Files
Vercel now serves eligible FastAPI assets from its CDN. See which requests stop using Functions and which protected paths still need them.

Vercel changed one cost line for FastAPI apps on September 10, 2026. Eligible frontend and static-file requests now go from Vercel's CDN straight to the visitor, so they stop creating Function invocations and compute usage. CDN requests and transfer still exist, which is why this cuts Function use instead of making static delivery free.
The request skips the Function now
A static file is something the server can send as-is, such as an HTML file, stylesheet, JavaScript bundle, font, or image. A Vercel Function is the running FastAPI process that handles dynamic requests. The CDN is the delivery layer close to the visitor.
Before this change, a file mounted through FastAPI could still take the Function path. The request reached Vercel's network, invoked the FastAPI Function, and let Python return the file.
Now Vercel finds eligible files behind app.frontend() and FastAPI's StaticFiles during the build. It promotes those files to the CDN, and matching requests are served without invoking the Function.
That changes the path from this:
visitor → CDN → FastAPI Function → file → visitor
to this:
visitor → CDN file → visitor
The dynamic API does not become static. Only matching files move. Files in the project-level public/ directory were already served from the CDN, so apps that put every public asset there do not get a new saving from this release.
Promoted directories also stay inside the Function bundle by default. Setting tool.vercel.fastapi.static.exclude = true removes that duplicate copy, but it is a separate bundle decision. You do not need it for the CDN request path to work.

The bill loses compute, not delivery
Vercel meters a request in layers. Function Invocations count requests that reach your code. Active CPU and Provisioned Memory measure the work and runtime resources behind those invocations. Fast Origin Transfer measures traffic between the CDN and the Function.
The outer delivery meters are different. CDN Requests, shown as Edge Requests in the dashboard, apply to both static assets and Function traffic. Fast Data Transfer measures the bytes sent between the CDN and the visitor.
Here is the clean ledger for one million otherwise identical asset requests that reached the Function before deployment and qualify for promotion after deployment:
The published Pro rate is $0.60 per one million Function invocations. So one million file requests that previously reached the Function remove $0.60 of gross invocation usage before the monthly credit, plus whatever Active CPU, Provisioned Memory, and Fast Origin Transfer those requests actually consumed.
That last part must come from your Usage data. A file response that barely touched Python has little CPU to remove. A large response can have more origin transfer to remove. The release gives you a new route, not a universal savings percentage.
CDN usage remains on the other side of the ledger. Vercel's current CDN pricing page lists the first 10 million Edge Requests and first 1 TB of Fast Data Transfer as included on Pro under on-demand billing, followed by region-based usage rates. Teams using Flat Rate CDN consume that plan's request and transfer capacity instead. The Flat Rate CDN explainer covers that separate budget decision.
For Hobby, the benefit can be headroom instead of a smaller cash bill. Hobby includes one million Function invocations, four Active CPU hours, and 360 GB-hours of Provisioned Memory. Public file traffic that no longer reaches the Function stops eating those allowances, while its CDN delivery still counts where applicable.
Eligibility follows your route and security rules
Vercel preserves FastAPI's declaration order for StaticFiles. A route declared before a static mount wins at the same path and reaches the Function. A route declared after the mount loses to the CDN file.
app.frontend() is slightly different. API routes always take priority over frontend files, regardless of declaration order. That keeps a frontend mounted at / from swallowing a real API endpoint.
Security changes the default too. A CDN file never enters Python, so FastAPI middleware and Depends() checks cannot run on it. Vercel therefore keeps these paths on the Function by default:
- All static mounts and frontends when the app has top-level middleware.
- A sub-app's static mounts when that sub-app has middleware.
- A frontend guarded by FastAPI dependencies.
Setting cdn = false makes the opposite trade. It disables promotion and keeps every matching request on the Function. That is a valid choice when application code must inspect each request.
There is one smaller edge case: the bare root of a StaticFiles mount still reaches the Function so FastAPI can redirect it to the trailing-slash form. Do not expect a literal zero in Function usage if clients keep requesting that bare path.
Where this changes the work
A solo SaaS founder with one FastAPI repo
A founder serving a bundled web app through app.frontend() can leave the API dynamic and move the public HTML, CSS, and JavaScript traffic off the Function path. The payoff is a cleaner compute baseline. Product traffic can grow without every browser asset fetch looking like backend work.
An agency engineer with public and client-only files
An agency engineer can place the public shell on the CDN while declaring a sensitive route before the static mount. The payoff is not just lower Function use. It is a route map that says which client files require Python to make an access decision and which do not.
A backend lead working with finance
A backend lead can give finance two separate forecasts: dynamic FastAPI traffic for the Function line, and all visitor traffic for the CDN line. The payoff is that a fall in Function Invocations is no longer mistaken for a matching fall in Edge Requests or transfer.
A team with global middleware
A team using top-level logging, tenant resolution, or authentication middleware should expect static mounts to stay on the Function by default. The useful move is to decide whether a clearly public asset area can bypass that middleware. If the answer is no, there is no safe saving to budget from this change.
Audit one deployment
Start from a real route inventory, not the assumption that every file with an extension is public.
Mark public and protected paths
List every
app.frontend()directory andStaticFilesmount. Mark the files that can be returned to any visitor. Separately mark anything whose response depends on a user, tenant, role, cookie, or request-time middleware result.Check declaration order
Keep a Function route before a
StaticFilesmount when that route must win. This is Vercel's documented pattern:Pythonfrom fastapi import FastAPI from fastapi.staticfiles import StaticFiles app = FastAPI() # Declared before the mount, so this route wins over any CDN file at this path. @app.get("/static/protected.json") def protected(): return {"access": "denied"} app.mount("/static", StaticFiles(directory="static"))Leave guarded paths on the Function
Check top-level middleware, sub-app middleware, and frontend dependencies before changing configuration. Do not set
cdn = trueto make a chart fall unless every affected file is genuinely public. Usecdn = falsewhen all matching traffic must run through FastAPI.Choose whether to trim the bundle
If the running app never reads the promoted files, you can exclude their source directories from the Function bundle with the documented setting:
TOML[tool.vercel.fastapi.static] exclude = trueLeave the default in place if runtime code still needs those files.
Deploy and verify the split
Run
vc deploy. Exercise one public file, one protected file, and one API route. Confirm the protected response still behaves correctly, then compare the project's Function Invocations, Active CPU, Provisioned Memory, Fast Origin Transfer, Edge Requests, and Fast Data Transfer in Usage. The public sample should leave the Function path without disappearing from the CDN totals.
The honest limit on the saving
The direct invocation price is only $0.60 per million on Pro. For a small app, the cash saving may be pennies or absorbed by the monthly usage credit. The larger benefit can be removing asset noise from Function usage and preserving compute headroom for real API work.
The saving grows when public asset traffic is large, the old Function path held memory long enough to matter, or responses created meaningful Fast Origin Transfer. It stays small when the files already lived in public/, traffic is low, or security rules correctly keep the paths on the Function.
This also does not replace asset optimization. The same large JavaScript bundle, image, or font still has to cross the CDN to the visitor. Shrinking those files remains the move that cuts Fast Data Transfer.
Your Monday move
Act this week if app.frontend() or StaticFiles serves public files and those requests show up in Function usage. Wait if the files are protected and you have not separated their access rules. You are unaffected if the relevant files already come from public/ or another CDN-backed path.
On Monday, inventory public versus protected asset paths, preserve every route and guard that must run, then verify one deployment with one request from each class. Budget the measured drop in Function usage. Keep CDN requests and transfer on the sheet.
If you want more plain-English breakdowns of platform changes that alter the operating bill, join the newsletter.
- Last Updated
- Sep 13, 2026
- Category
- Explained







