Vercel CDN skips Vary: Cookie responses: Sume status proxy headers
Vercel's CDN no longer caches Vary: Cookie responses. For a route proxying Sume job status, send Cache-Control private and honor next_poll_after_seconds.

A route that proxies Sume job status is personal to the caller, so it should not sit in a shared cache whatever Vercel's CDN does. Send Cache-Control: private, no-store on it and let the client honor next_poll_after_seconds.
Vercel facts are from its September 30 changelog entry; Sume facts from Communication modes and Authentication, read 2026-10-01.
What changed on Vercel?
The entry says the CDN no longer caches origin responses when Vary includes Cookie. To spot it, look for x-vercel-cache: MISS and the Runtime Logs reason vary_key_denied:cookie. The fix depends on the response: if it does not depend on cookies, remove Cookie from Vary; if it is personalized, keep Vary and add Cache-Control: private. Caching for other supported Vary headers is unchanged.
| Response | Action |
|---|---|
| Does not depend on cookies | Remove Cookie from Vary |
| Personalized | Keep Vary, add Cache-Control: private |
Is a Sume status route cacheable?
Not in a shared cache. Job status changes as the job moves through queued, processing and a terminal state, and it belongs to one workspace. Caching it would serve one caller's job to another or hold a stale processing. The Sume status payload's next_poll_after_seconds tells the client when to ask again; that is the pacing signal, not a CDN TTL.
What does the route send?
Forward the status body and set private headers. Check that the caller owns the job id before you proxy it; the example leaves that to your auth layer.
export async function GET(
_req: Request,
{ params }: { params: Promise<{ id: string }> },
) {
const { id } = await params;
const res = await fetch(
"https://api.sume.com/v1/jobs/" + encodeURIComponent(id) + "/status",
{ headers: { "x-api-key": process.env.SUME_API_KEY ?? "" }, cache: "no-store" },
);
return new Response(res.body, {
status: res.status,
headers: {
"content-type": "application/json",
"cache-control": "private, no-store",
},
});
}How much polling can the proxy afford?
One key has one budget. Reads and writes are separate, with Free at 4,800 reads and 120 writes per minute and Scale at 48,000 and 1,200, so a busy status route cannot 429 your submits. Still, many browsers behind one key share that read budget; see the read and write limits post.
Sources
Related posts
More in Developers
- Crop video size: the 0.05 minimum side in video-filter
A video-filter crop needs width and height of at least 0.05 and a rectangle inside the frame; anything else returns video_filter_crop_out_of_bounds.
- FFmpeg darken video: why Sume refuses dim amount 0
A dim amount of 0 is refused: the range is above 0 up to 1, and 0 or above 1 returns video_filter_amount_out_of_range. Check the program free before you encode.
- ffmpeg filtergraph too long: video-filter's 2048 and 32 limits
A Sume video-filter filtergraph is capped at 2048 characters and 32 named filters. Past either, or with an unknown filter, you get invalid_filtergraph.
- ffmpeg filter_complex [0:v] in video-filter: refused, labels fine
Sume video-filter refuses stream specifiers like [0:v] in a filtergraph because the server wraps the input and output. Internal labels such as [a] are allowed.
Written by Sume