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
- Remove the generic
/api/:path* cache rule entirely.
- 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.
- 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
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/
Summary
next.config.mjsappliesCache-Control: public, s-maxage=60, stale-while-revalidate=300to 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:This catch-all rule covers:
GET /api/notify— returnstelegramConfigured: true/false, subscriber count, and broadcast preview text.GET /api/alerts— returns all alert configs, destination chat IDs, andtelegramConfigured: 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
proxy_cachesitting in front of this app will cache theGET /api/alertsresponse (which includes all registered alert destination chat IDs) and serve it to every subsequent visitor for up to 5 minutes stale.GET /api/notifyresponses containing notification previews are cached and served to unrelated users.Cache-Control: publicdirective explicitly tells intermediaries that the response is safe to share across all users — this is not true for any of these endpoints.feed.xdc.networkbehind nginx), the nginx config inDEPLOYMENT.mddoes not configureproxy_cache, but any CDN placed in front (Cloudflare, Fastly, etc.) would honour thes-maxage=60directive 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
/api/:path*cache rule entirely./api/signals,/api/markets,/api/earthquakes,/api/conflicts— these are appropriate forpubliccaching.Cache-Control: no-store, privateto all notification, alert, and push endpoints.Acceptance criteria
GET /api/alertsresponse is not publicly cacheable (no-storeorprivate).GET /api/notifyresponse is not publicly cacheable.GET /api/pushresponse is not publicly cacheable.publiccache rule remains for/api/:path*.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.mjscontains the wildcard rule in current HEAD.