A small Flarum extension that adds server-side response caching to the JSON:API. You configure regex-pattern rules with per-rule TTLs to short-circuit expensive endpoints (top-poster widgets, statistics aggregations, public discussion lists) before they touch the database.
Why
Flarum re-aggregates SQL on every request to certain API endpoints. The canonical example is /api/users?filter[top_poster]=true, hit on every page load by widgets like afrux/top-posters-widget, even though the answer only changes once an hour.
Edge-caching at Cloudflare is the wrong layer (and Set-Cookie blocks caching on free/pro plans anyway). The right fix is caching inside Flarum, where rules can be tuned per-forum in admin and the cache key can include exactly what matters.
Features
Admin UI to add, remove and toggle rules. No code edits.
Path pattern (PCRE regex, required) and query filter (optional regex). First match wins, priority-ordered.
Per-rule TTL (1 second to 7 days).
public or guest scope. public shares the cached response across all visitors. guest only uses the cache when the request has no session.
Per-rule "guest-only seed" mode so authenticated responses never land in a shared bucket. Closes the "admin response with email included gets read by guests" hole on endpoints like /api/users.
Automatic Redis backend if REDIS_HOST is set in the container env. Otherwise transparent fallback to Flarum's file cache.
Honours Cache-Control: private and no-store from route handlers, so any other extension can opt out per-response.
Strips Set-Cookie from cached responses (no session leakage).
Refuses scope=public on known per-user paths (/api/users/<id>, /api/notifications, /api/preferences, /api/access_tokens) with a clear admin error pointing you at scope=guest.
Debug headers (X-BrynForum-Cache: HIT|MISS|SKIP-* and X-BrynForum-Cache-Backend: redis|file) so you can verify the rules are doing what you expect.
Install
composer require brynforum/api-cache
Then enable API Cache under Admin → Extensions.
Example rules
| Use case | Path pattern | Query filter | TTL | Scope |
| Top-poster widget | #^/api/users$# | #filter\[top_poster\]=true# | 600 | public |
| Forum index (guests) | #^/api/discussions$# | (empty) | 60 | guest |
| Tag list | #^/api/tags$# | (empty) | 3600 | public |
| Site statistics | #^/api/statistics$# | (empty) | 300 | public |
Screenshots

Cache Response headers:

Links