---
title: How to handle order-ahead traffic spikes on Vercel
description: Order-ahead traffic spikes hit the same routes every lunch hour. Learn how to scale, cache, queue, and test a restaurant ordering system on Vercel.
url: "https://vercel.com/kb/guide/handle-order-ahead-traffic-spikes-on-vercel"
published: 2026-09-28
last_updated: 2026-09-28
authors: Vercel
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

A quick-service restaurant (QSR) ordering system fails in a narrow window. Lunch traffic arrives on a schedule, a promo drop lands on top of it, and the checkout path saturates before the menu does. The routes are predictable, so you can build for them ahead of time.

## Why order-ahead traffic spikes break a restaurant ordering system

The load concentrates on three routes: the location menu, the cart, and checkout.

Three limits decide when those routes fail, in this order:

- **Function concurrency:** Menu and checkout invocations climb together, and checkout holds each instance open longer.
  
- **Upstream dependencies:** The point-of-sale (POS) system and the paymeVnt processor have their own rate limits, which are usually lower than yours.
  
- **Cache misses:** A menu that regenerates per request turns one traffic spike into thousands of identical origin calls.
  

A customer who cannot complete an order inside a promo window does not come back later in the day, so every fix below protects checkout first, starting with how much traffic a function can absorb.

## How Vercel scales functions during a lunch rush or promo drop

[Vercel Functions](https://vercel.com/docs/functions) scale automatically, so there is no pre-provisioning step before a known peak. The ceilings and the ramp rate decide how much of a spike reaches your functions at once.

Automatic concurrency scaling applies these limits:

- **Burst rate:** Vercel raises the concurrency limit by up to 1,000 concurrent executions per 10 seconds, per region, when it detects a spike.
  
- **Maximum concurrency:** Functions scale to 30,000 on Hobby and Pro, or 100,000 on Enterprise. Enterprise teams can purchase extended concurrency.
  
- **Throttling behavior:** Exceeding the limit returns a `503 FUNCTION_THROTTLED` error.
  

That ramp takes several minutes during a substantial surge, so a promo that goes from zero to peak in seconds depends on how much work each invocation avoids.

[Fluid compute](https://vercel.com/docs/fluid-compute) cuts that work on the checkout path. Multiple invocations share a single function instance, so while one waits on a POS response, another runs in the same instance. Vercel prioritizes idle resources before allocating new ones and reduces cold starts through bytecode caching and function pre-warming on production deployments. Optimized concurrency is available on the Node.js and Python runtimes.

The framework version matters at peak too. Next.js 16.3 replaced web streams with native Node.js streams in the App Router rendering layer, so apps handle up to 22% more requests under load with no application changes.

Caching removes the work entirely, because most menu requests should never reach a function at all.

## How to cache menus by location so peak traffic never reaches your origin

Menu data varies by location and daypart, and kitchen availability changes within minutes. That combination rules out both a fully static build and a per-request fetch.

[Incremental Static Regeneration](https://vercel.com/docs/incremental-static-regeneration) (ISR) fits this shape. Visitors get the cached menu immediately while Vercel regenerates it in the background.

With Cache Components in Next.js 16.3, that extends to locations you never prerender. Prerender your busiest stores through `generateStaticParams`, and the rest serve an instant loading shell on the first visit and upgrade to the fully prerendered page in the background, so a chain with hundreds of locations does not have to build all of them.

Vercel’s content delivery network (CDN) adds three behaviors that matter at peak:

- **Request collapsing:** When many requests hit the same uncached path at once, Vercel [collapses them](https://vercel.com/docs/incremental-static-regeneration/request-collapsing) into a single function invocation per region.
  
- **Globally consistent purging:** When you revalidate, every region updates within 300ms, and Vercel purges HTML and data payloads together.
  
- **Stale-on-failure:** If a revalidation fails, Vercel keeps serving the cached version and retries after 30 seconds.
  

Together they let one location menu absorb hundreds of simultaneous requests at 11:30 without your POS seeing more than one fetch per region.

### Tag menu data by location and item

In Next.js 16, [Cache Components](https://nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) makes the cache key granular enough to purge one item at one store.

Enable it in `next.config.ts`, alongside the partial prefetching flag that Next.js 16.3 pairs with it:

```typescript
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  cacheComponents: true,
  partialPrefetching: true,
};

export default nextConfig;
```

Cache Components requires the Node.js runtime, and it implements Partial Prerendering (PPR) as the default behavior in the App Router. Partial prefetching lets a link prefetch only the loading shell of the page behind it, which matters on the tap-heavy path from menu to modifier to cart.

Inside a `'use cache'` function, tag each menu fetch by both item and location:

```tsx
import { cacheTag, cacheLife } from 'next/cache';

export async function getLocationMenu(locationId: string) {
  'use cache';
  cacheLife('max');
  cacheTag(`location-${locationId}`);

  const res = await fetch(`https://pos.example.com/menu/${locationId}`);
  const menu = await res.json();

  for (const item of menu.items) {
    cacheTag(`menu-item-${item.id}`);
  }

  return menu;
}
```

The `max` profile uses long cache lifetimes. Your menu management system fires a webhook, so a price change or an item going out of stock purges only what changed.

### Choose the right invalidation function

Two functions invalidate a tag, and they behave differently under load:

- `revalidateTag(tag, 'max')`**:** Serves the cached menu while fetching fresh data in the background. Use it in route handlers and webhooks, which is where menu updates arrive.
  
- `updateTag(tag)`**:** Expires the data immediately, so the next request waits for fresh data. It can only be called from a Server Action, and it exists for read-your-own-writes cases such as a customer editing their cart.
  

The trap is omitting the profile, because `revalidateTag(tag)` alone falls back to legacy behavior equivalent to [`updateTag`](https://nextjs.org/docs/app/api-reference/functions/updateTag) and forces a blocking fetch on the next request during a rush.

## How to keep order submission durable during a traffic spike

The response to a submitted order should confirm receipt and nothing more. A checkout function that waits on the POS system and the payment processor in sequence inherits the latency and failure rate of both, and adding a loyalty service compounds them.

[Vercel Queues](https://vercel.com/docs/queues), currently in beta, is the primitive for that decoupling. It provides at-least-once delivery, so a retry can deliver the same order twice and consumers need to be idempotent. Publishing with an idempotency key handles that for you. Vercel deduplicates the message for the entire lifetime of the original, which is anywhere from 60 seconds to 7 days depending on the retention you set. The default retention is 24 hours.

Configure a consumer with a queue trigger in `vercel.json`:

```json
{
  "functions": {
    "app/api/queues/fulfill-order/route.ts": {
      "experimentalTriggers": [
        {
          "type": "queue/v2beta",
          "topic": "orders",
          "retryAfterSeconds": 60
        }
      ]
    }
  }
}
```

Consumer functions configured this way have no public URL and can only be invoked by Vercel's queue infrastructure, so they need no authentication logic of their own. Vercel retries a failed message at your configured delay until it expires or reaches the trigger's `maxDeliveries`, which is unlimited by default, so set that option to bound what a failing consumer costs you during a rush.

For an order that touches several services in sequence, [Vercel Workflows](https://vercel.com/docs/workflows) sits on top of Queues and keeps state between steps, the higher-level choice for most [durable background work](https://vercel.com/kb/guide/how-to-run-background-jobs-in-nextjs-on-vercel). Mark the flow `'use workflow'` and each external call `'use step'`, and every step persists and resumes across a crash or a deploy. Steps retry up to 3 times by default before the error propagates to the workflow.

`waitUntil` and `after()` are not durable substitutes. Promises passed to `waitUntil` share the function's timeout, Vercel cancels them when the function times out, and neither carries a retry guarantee. Use them for analytics and logging, not for an order someone already paid for.

## How to reduce ordering friction under load

Backend capacity does not help if the menu itself feels slow. The delay a customer notices sits between a tap and the screen responding to it.

[Interaction to Next Paint](https://vercel.com/docs/speed-insights/metrics) (INP) measures that gap, from a tap through JavaScript to the next painted frame. Vercel considers [200 milliseconds or less](https://web.dev/articles/inp) good, so a modifier toggle that takes 400ms to reach the cart needs work.

INP breaks into [three phases](https://vercel.com/kb/guide/how-to-improve-core-web-vitals), each with a fix that lands differently on a menu:

- **Input delay:** Long tasks over 50ms block the main thread. Defer non-critical scripts and yield with `scheduler.yield()`.
  
- **Processing duration:** Expensive Document Object Model (DOM) reads and writes cost time. Batch mutations and push updates through `requestAnimationFrame`.
  
- **Presentation delay:** Large DOM trees slow the paint. Apply `content-visibility: auto` to off-screen menu rows and wrap cart updates in React's `useTransition`.
  

PPR covers the server half of the same problem. The static menu shell ships from the CDN immediately while per-location pricing, availability, and cart state stream into Suspense boundaries as the POS answers, so a slow availability call never blocks the shell.

To find which control is slow rather than which page, use [Speed Insights Plus](https://vercel.com/docs/speed-insights/limits-and-pricing), which reports breakdowns by element selector. It costs $10 per project per month on Pro and is included on Enterprise. A quantity stepper or a modifier toggle then shows up by name instead of inside an aggregate INP number. In development, the Instant Insights panel in Next.js DevTools surfaces navigations that are not instant, which catches a slow menu-to-cart transition before it ships.

## How to test an ordering system for peak readiness

Knowing that Vercel scales does not tell you that your ordering system does. A menu page can pass a load test while checkout slows under concurrency, because checkout also runs inventory checks, promo code logic, and payment initiation.

Vercel's [load testing policy](https://vercel.com/kb/guide/what-s-vercel-s-policy-regarding-load-testing-deployments) sets the constraints.

Confirm all four before you schedule a test:

- **Plan requirement:** Vercel permits load testing on Enterprise plans only.
  
- **Prior approval:** Every test needs advance coordination with your Vercel representative, who routes the timing, request volume, source IP addresses, and target hostnames to the Infrastructure team.
  
- **High-volume tests:** Any test above 50,000 requests per second requires a ticket with Vercel's engineering team.
  
- **Unannounced tests:** Test traffic without prior notice reads as abnormal traffic, and Vercel blocks the source IP addresses shortly after the test starts.
  

Run simulations against a preview deployment loaded with production-like menu and order fixtures, ramp gradually rather than jumping straight to peak, and target the dynamic routes rather than static asset delivery.

Navigation speed needs its own regression test. The `instant()` helper in `@next/playwright` asserts what a customer sees immediately after a tap, so a refactor that moves a Suspense boundary and slows the cart fails in CI instead of during a promo.

Set your alerting thresholds while the dashboard is still green. [Vercel Observability](https://vercel.com/docs/observability) exposes ISR reads and writes per route, in-function concurrency, and external requests by hostname, and [Drains](https://vercel.com/docs/drains), available on Pro and Enterprise, route structured logs to whatever your on-call rotation already watches. Error rate, function duration, and ISR revalidation latency each need a number that pages someone.

## What to verify before a promo drop goes live

Three platform behaviors have failure modes specific to ordering flows.

Confirm each one before the promo goes out:

- **Routing Middleware runtime:** Next.js 16 renamed `middleware.ts` to `proxy.ts` and changed the exported function from `middleware` to `proxy`. [Routing Middleware](https://vercel.com/docs/routing-middleware) now runs on Node.js only, so store selection or geo-routing that ran on the Edge Runtime has to migrate first.
  
- **Deploy safety for long carts:** [Skew Protection](https://vercel.com/docs/skew-protection), available on Pro and Enterprise, pins framework-managed requests such as Server Actions and prefetches to the deployment that served the initial page load. It does not pin full-page navigations by default, so a customer mid-checkout gets a reload after a deploy unless you pin the session with the `__vdpl` cookie.
  
- **Bot traffic on checkout:** [BotID](https://vercel.com/docs/botid) runs an invisible challenge on high-value routes with no visible CAPTCHA. Basic checks are free on every plan, and Deep Analysis costs $1 per 1,000 `checkBotId()` calls on Pro, with custom pricing on Enterprise.
  

The default max age for Skew Protection is one day from deployment creation, which is worth raising if your checkout sessions run long or your deploy cadence is high.

## Next steps

Once you have made the caching, queueing, and monitoring decisions, the remaining work is standing the project up.

[Start a new project](https://vercel.com/new), or [browse the templates](https://vercel.com/templates) for a framework-ready starting point.

## Related resources

- [Incremental Static Regeneration](https://vercel.com/docs/incremental-static-regeneration)
  
- [Vercel Queues](https://vercel.com/docs/queues)
  
- [Vercel Workflows](https://vercel.com/docs/workflows)
  
- [Concurrency scaling](https://vercel.com/docs/functions/concurrency-scaling)
  
- [Black Friday preparation](https://vercel.com/kb/guide/black-friday-preparation)
  
- [Vercel's load testing policy](https://vercel.com/kb/guide/what-s-vercel-s-policy-regarding-load-testing-deployments)