Skip to content

next.config.mjs wildcard Cache-Control: public applied to all /api/* routes including notification, alert, and Telegram endpoints #63

Description

@tg12

Summary

next.config.mjs applies Cache-Control: public, s-maxage=60, stale-while-revalidate=300 to every route under /api/:path*. This blanket cache header is applied to notification endpoints, push subscription state, alert configuration readers, and Telegram status — all of which must never be publicly cached. Any CDN, reverse proxy, or shared cache between the client and server will store and serve these responses to all users.

Evidence

File: next.config.mjs:

{
  source: '/api/:path*',
  headers: [{ key: 'Cache-Control', value: 'public, s-maxage=60, stale-while-revalidate=300' }],
}

This catch-all rule covers:

  • GET /api/notify — returns telegramConfigured: true/false, subscriber count, and broadcast preview text.
  • GET /api/alerts — returns all alert configs, destination chat IDs, and telegramConfigured: true.
  • GET /api/push — returns { subscriptions: N, vapidConfigured: true/false }.

More specific cache rules exist for /api/signals, /api/markets, and /api/flights, but the generic /api/:path* rule applies to everything else, and Next.js processes headers in order — so the generic rule applies to all endpoints not specifically overridden.

Why this matters

  • A CDN or nginx proxy_cache sitting in front of this app will cache the GET /api/alerts response (which includes all registered alert destination chat IDs) and serve it to every subsequent visitor for up to 5 minutes stale.
  • GET /api/notify responses containing notification previews are cached and served to unrelated users.
  • The Cache-Control: public directive explicitly tells intermediaries that the response is safe to share across all users — this is not true for any of these endpoints.
  • On the demo deployment (feed.xdc.network behind nginx), the nginx config in DEPLOYMENT.md does not configure proxy_cache, but any CDN placed in front (Cloudflare, Fastly, etc.) would honour the s-maxage=60 directive and cache the responses.

Root cause

A convenience cache policy for static public data (/api/signals, /api/markets) was applied via an overly broad wildcard that captures all API routes including sensitive administrative ones. No per-endpoint override was added for the sensitive endpoints.

Recommended fix

  1. Remove the generic /api/:path* cache rule entirely.
  2. Apply cache headers explicitly only to the endpoints that should be publicly cacheable and have only public data:
    • /api/signals, /api/markets, /api/earthquakes, /api/conflicts — these are appropriate for public caching.
  3. Add explicit Cache-Control: no-store, private to all notification, alert, and push endpoints.
// Explicit no-cache for sensitive endpoints
{ source: '/api/notify', headers: [{ key: 'Cache-Control', value: 'no-store, private' }] },
{ source: '/api/alerts', headers: [{ key: 'Cache-Control', value: 'no-store, private' }] },
{ source: '/api/push', headers: [{ key: 'Cache-Control', value: 'no-store, private' }] },
{ source: '/api/telegram', headers: [{ key: 'Cache-Control', value: 'no-store, private' }] },

Acceptance criteria

  • GET /api/alerts response is not publicly cacheable (no-store or private).
  • GET /api/notify response is not publicly cacheable.
  • GET /api/push response is not publicly cacheable.
  • No catch-all public cache rule remains for /api/:path*.
  • Verified: placing a CDN in front does not serve cached notification data to different users.

Suggested labels

security, bug

Priority

P0

Severity

High — misconfigured cache policy exposes notification state and Telegram configuration to public CDN caching across all users.

Confidence

Confirmed — next.config.mjs contains the wildcard rule in current HEAD.


This issue was filed as part of the AI Slop Intelligence Dashboard Awareness Program.
Repo under review: https://github.com/OpenScanAI/GlobeNewsLive
Programme: https://labs.jamessawyer.co.uk/ai-slop-intelligence-dashboards/

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions