Next.js 16.3.8 fixes ISR cache poisoning: keep job pages dynamic
Next.js v16.3.8 fixes cache poisoning in SSG and ISR and Draft Mode leaks. Why a Sume job status page should stay dynamic and uncached, whatever the version.

The Next.js releases page lists v16.3.8, printed on September 30, as fixing cache poisoning in SSG and ISR and leaks in Draft Mode, alongside an SSRF fix in Image Optimization and a low-severity dev-server MCP disclosure. If you show a Sume render's progress on a Next.js page, keep that page dynamic and uncached, and upgrade to the patched release.
A job status is per-request, changing data. It is the wrong thing to put behind a static or incremental cache even on a version with no known bug.
What the release entry lists
Severities are as the page printed them. I read the page on 2026-10-03 and did not read the advisories behind each line, so check them for affected versions.
| Area | Issue | Severity on the page |
|---|---|---|
| Image Optimization | SSRF | High |
| SSG and ISR | Cache poisoning | Not stated in the entry read |
| Draft Mode | Leaks | Not stated in the entry read |
| Dev server MCP | Information disclosure | Low |
Why job status should never be cached
Sume's docs say a job is created as a durable record and that you poll status_url until terminal is true, then read result_url. Status moves from queued to processing to a terminal value, so a cached copy shows the wrong state, and in the worst case shows one user's job to another. Cache poisoning bugs are a reminder that shared caches are an attack surface; the safest status page is one the cache never sees.
A second reason is that a result belongs to one job, so a statically generated page that embeds a result URL at build time is a pattern to avoid.
A dynamic route that polls on the server
This route handler forces dynamic rendering, reads the job status with the key held on the server, and sends the browser no-store. It uses GET /v1/jobs/:id/status from the Sume API reference.
// app/api/jobs/[id]/status/route.ts
export const dynamic = "force-dynamic";
export async function GET(
_req: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
const key = process.env.SUME_API_KEY;
if (!key) return new Response("missing key", { status: 500 });
const r = await fetch(
`https://api.sume.com/v1/jobs/${encodeURIComponent(id)}/status`,
{ headers: { Authorization: `Bearer ${key}` }, cache: "no-store" }
);
return new Response(await r.text(), {
status: r.status,
headers: {
"content-type": "application/json",
"cache-control": "no-store",
},
});
}Webhook handlers are dynamic too
A webhook receiver should never be cached either, and it should read the raw body, because Sume signs the raw JSON body with HMAC SHA 256 over <timestamp>.<raw_body>. Return a 2xx only after storing the event, and treat job_id as your idempotency key, as the webhook docs state. Keep status_url polling as a backup for deliveries that never arrive.
- Upgrade to v16.3.8 or later and read the advisories for your affected range.
- Mark job status and webhook routes dynamic and send
cache-control: no-store. - Keep the Sume API key in server environment variables, never in a client bundle.
- Never render a result URL at build time.
Upgrading safely
After you upgrade, test the status route with two different job ids from two different sessions and confirm each response matches its own job. That check is cheap and catches the exact class of bug a poisoned cache would produce. Also confirm that no page imports the API key into client code, because a dynamic route does not help if the key ships in a bundle.
If you proxy generated images through Next.js image optimization, the same release lists an SSRF fix there; keep your allowed remote patterns narrow and limited to the Sume media host you actually use.
Sources
Related posts
More in Developers
- Which Node versions to test the Sume SDK on: 22, 24 and 26
Node 26.10.0 is Current, 24.21.0 and 22.23.3 are LTS. A small CI matrix and smoke test for code that calls the Sume API with fetch and WebCrypto.
- OpenAI Agents API sandbox: keep the Sume API key out of it
The OpenAI Agents API beta adds a sandbox and hosted-browser computer use. Where a Sume API key can live when an agent runs there, and what to hand it instead.
- OpenAI Realtime GA migration: drop the OpenAI-Beta header
Beta Realtime integrations must move to GA and stop sending OpenAI-Beta: realtime=v1. A short checklist, a code scan for the header, and where Sume jobs fit.
- OpenAI TTS: 13 built-in voices vs 9 legacy ones, pick the model first
OpenAI's guide lists gpt-4o-mini-tts with 13 built-in voices and legacy tts-1 and tts-1-hd with 9. Formats, custom voice consent and disclosure, as a checklist.
Written by Sume