ekumanov/flarum-ext-edge-cache
Composer 安装命令:
composer require ekumanov/flarum-ext-edge-cache
包简介
Cookieless guest page views + CSRF retry shim, enabling safe Cloudflare edge caching of guest HTML.
README 文档
README
Makes guest page views cookieless so Cloudflare can safely cache guest HTML —
discussion pages, the index, and tag pages — at the edge, plus a client-side
CSRF retry shim so auth flows survive landing on a cached page. It also
preloads the discussion page's boot-critical JS chunks, emits Link: rel=preload headers for Cloudflare Early Hints, can purge Cloudflare on
every content change so a long edge TTL never serves stale content, and
pre-paints the server-rendered discussion content for guests so the first
post is visible (and is the LCP) at first paint instead of after the SPA
boots. Requires Flarum 2.0.
Installation
composer require ekumanov/flarum-ext-edge-cache
Components
- EdgeCacheMiddleware (forum frontend, inserted before
StartSession— StartSession attaches cookies on the response's way OUT, so only an outer middleware can strip them): credential-less GET/HEAD on an allowlisted path (/d/*, the index/, and/t/*) → strip ALLSet-Cookie+X-CSRF-Token, emit aServer-Timing: originheader andCache-Control: public, s-maxage=<ttl>, max-age=0, must-revalidate. The edge TTL is 3600s when Cloudflare purge credentials are configured (see below) — purge-on-write keeps the long tail warm without stale content — and a conservative 300s otherwise. All other forum HTML → explicitCache-Control: private, no-store. - JS retry shim: on a
400whose JSON:API body carriescode: csrf_token_mismatch, single-flightGET /api(refreshes session cookie + token via core's response-header update), then retry the original request once. This covers/api/*writes as well as login and register: although those POST to forum routes, Flarum 2.0's forum error handler content-negotiates, so an XHR (default catch-allAccept) receives the same JSON:API error the shim matches. - CSRF exemption for
forum-widgets.guest-heartbeat, the guest presence beacon of ekumanov/flarum-ext-forum-widgets (spoofable anyway, and the highest-frequency 400 source on cached pages). A no-op when that extension isn't installed. - Discussion chunk preload: on
/d/*, emits<link rel="preload" as="script">for core'sPostStream.js+PostStreamScrubber.js(hashes read fromrev-manifest.jsonat runtime, so the preload URL always matches what the webpack runtime fetches — no double download). These chunks are otherwise fetched serially after boot; preloading lets them download in parallel withforum.js, collapsing that render-delay tail. Helps guests and logged-in members alike. - Cloudflare purge-on-write: when a discussion's content changes (post
added/edited/deleted/hidden/restored, discussion renamed/deleted/hidden/
restored), queues a purge of every cached page the write invalidates: the
discussion's canonical landing URL, its slugless
/d/{id}variant (served directly, so it caches under its own key), the forum index, and the landing pages of its tags and their parents. Lets the edge TTL stay long without guests seeing stale pages. Best-effort and queued, so a Cloudflare hiccup never blocks the user's action; a no-op (with a log line) when no credentials are configured. - Early Hints
Linkheaders: every 200 HTML response — cacheable or private — carriesLink: <…>; rel=preloadheaders forforum.css,forum.js, the active locale bundle, and (on/d/*) the two PostStream chunks. With Early Hints enabled on Cloudflare (Speed → Optimization; available on the Free plan) these are replayed as a103 Early Hintsresponse while the origin is still rendering, so the browser downloads the render-critical assets during server think-time. That helps most exactly where the edge cache can't: logged-in members (always DYNAMIC) and cold-MISS guests. Browsers de-duplicate against the identical in-HTML references, so where 103 isn't supported the headers are inert. - Visible pre-paint (guests,
/d/*): Flarum already server-renders the discussion title + posts into<noscript id="flarum-content">— JS-enabled browsers never paint it. This swaps the content wrapper view so that same markup is emitted as a visible, styled block instead, and an inline MutationObserver removes it on the first childList mutation of#content— a microtask, i.e. in the same rendering frame in which Mithril's first render lands, so exactly one of (pre-paint | hydrated page) is ever painted. The first post's text becomes the LCP at ≈first paint; the hydrated repaint is equal-or-smaller so it cannot re-trigger LCP (removed elements remain LCP candidates in Chromium). In lab testing at a phone-calibrated CPU throttle this collapsed the LCP−FCP gap from ~3s to 0 (LCP −58%) with zero CLS across the swap. If JS never boots, the block simply stays — a strictly better no-JS story than core's<noscript>. Media discipline while the block is visible: images wrapped by ekumanov/flarum-ext-cls-fix keep their reserved aspect-ratio boxes (its CSS is unscoped on purpose), emoji are size-stable via CSS, s9e MediaEmbed iframes sit in responsive padding-box wrappers and areloading="lazy"; any other unsized<img>is hidden inside the pre-paint, which only shortens the block — the safe direction. Post images additionally get theirsrcrenamed in the pre-paint copy (the placeholder box stays) so nothing fetches during the critical window and the hydration timeline is byte-identical to a non-pre-painted load, and below-fold posts are render-skipped viacontent-visibility: autoso a 20-post page costs no more main-thread time than a short one. Members are never pre-painted (their hydrated page differs from the guest render).
The matching Cloudflare Cache Rule (v2)
Expression: host eq "example.com" AND (path eq "/" OR starts_with(path, "/d/")
OR starts_with(path, "/t/")) AND method GET AND NOT (cookie contains
"flarum_session" OR cookie contains "flarum_remember" OR cookie contains
"locale") → Eligible for cache, Edge TTL: respect origin, Browser TTL:
respect origin. Adjust the host and the paths to your install (e.g. path eq
"/forum/" plus prefixes /forum/d/, /forum/t/ when Flarum is mounted under
/forum). The locale clause keeps a language-switched guest render off the
shared cache — it matches both the bare locale cookie and prefixed variants
like flarum_locale, in lockstep with the origin (see Invariants).
Upgrading from the v1 rule (/d/ only): deploying the extension first is
safe — Cloudflare doesn't cache HTML without a matching rule, so the wider
origin allowlist is inert for the extra paths until the rule covers them.
Extend the rule right after deploying, then confirm cf-cache-status: MISS → HIT on /. Enable Early Hints (Speed → Optimization) at the same
time to activate the Link-header preloads.
Cloudflare credentials (for purge-on-write + the long TTL)
Add to config.php (kept server-side, never exposed to the frontend):
'cloudflare' => [ 'zone_id' => '...', 'api_token' => '...', // a token scoped to Zone → Cache Purge for this zone ],
Create a scoped token in the Cloudflare dashboard (My Profile → API Tokens → Create Custom Token → permission Zone : Cache Purge, restricted to this zone) — do not use a Global API Key. Without these keys the extension keeps the conservative 300s edge TTL and the purge listener is a logged no-op, so it is safe to install before (or without) Cloudflare.
Known staleness
Purge-on-write fires on discussion content events only (post
added/edited/deleted/hidden/restored, discussion renamed/deleted/hidden/
restored). Anything else embedded in a cached guest payload refreshes only
within the edge TTL, not instantly — e.g. sticky/lock state, tag moves, poll
votes, and like counts. Likewise, after a rename the old-slug URL keeps
serving its cached page (then 301s) until it ages out under the TTL; after a
re-tag, the OLD tag's landing page waits for the TTL (or the next write that
touches that tag). Index/tag sort variants (?sort=…) cache under their own
keys and are not purge-enumerable either. All of this is bounded by the edge
TTL by design; the TTL is the freshness floor for everything the purge list
doesn't enumerate.
Invariants — read before changing anything
- The middleware path allowlist and the CF rule scope move in lockstep, in the same deploy.
- API responses must keep their Set-Cookie forever (heartbeat session-dedupe and the shim's refresh GET depend on it). This middleware is forum-only.
/reset,/confirmetc. are server-rendered Blade forms needing their session cookie — permanently denylisted.- A guest-facing language switcher would poison the shared cache (CF ignores
Vary). Origin-side this is now enforced — a request carrying alocalecookie is servedprivate, no-store— but keep the CF rule in lockstep (it must also exclude thelocalecookie, as above). - CSRF 400s never reach flarum.log (KnownError) — monitor nginx access-log double-400s instead.
- The pre-paint's LCP lock depends on one geometry rule: the pre-paint stack
above the first post must sit equal-or-HIGHER than the hydrated one (the
CSS deliberately undershoots by ~10px), so the pre-paint copy of the LCP
paragraph is always the equal-or-larger paint. If a theme change makes the
pre-paint sit lower, nothing breaks visually — but LCP silently returns to
hydration time. Re-verify after theme/hero changes (the class docblock in
PrePaintDiscussiondocuments the tuning). - The pre-paint view must keep the
#flarum-loading,#flarum-loading-errorand#flarum-contentelement ids — core's boot scriptsgetElementByIdthem unconditionally.
Rollback order
Disable this extension → clear the Flarum cache and purge the Cloudflare cache immediately (cached HTML referencing a rebuilt forum.js without the shim would otherwise strand guests until TTL expiry). Deleting the CF rule is safe at any point, in any order.
Build
cd js && npm install && npm run build
ekumanov/flarum-ext-edge-cache 适用场景与选型建议
ekumanov/flarum-ext-edge-cache 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 2 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 06 月 12 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「performance」 「cache」 「extension」 「cloudflare」 「flarum」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 ekumanov/flarum-ext-edge-cache 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 ekumanov/flarum-ext-edge-cache 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 ekumanov/flarum-ext-edge-cache 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
g4 application profiler package
repository php library
CSS/Javascript Minificator, Compressor and Concatenator for TYPO3 - highly configurable frontend asset optimization for CSS/JS merging, minification and compression with optional body parsing, async/defer loading, inline output, data-ignore exclusions, SRI integrity validation/calculation, external
WordPress mu-plugin to remove jQuery Migrate from the list of jQuery dependencies and to allow jQuery to enqueue before </body> instead of in the <head>.
A custom URL rule class for Yii 2 which allows to create translated URL rules
Create link to static resources with cache-breaking segment based on md5 of the file
统计信息
- 总下载量: 2
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 39
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-12