Edge caching integration v26.5+
Maho dispatches a core event that lets full-page-cache (FPC) or edge-cache modules opt out of session start for cacheable anonymous GETs, without forking core controllers.
Why session start matters for caching
Mage_Core_Controller_Varien_Action::preDispatch() always starts the session unless FLAG_NO_START_SESSION was set in the constructor. Today that flag is only set in 3 controllers (Api + 2 OAuth), so for anonymous traffic on CMS, category, or product routes, Maho:
- emits
Set-Cookie: <session>on the response, which makes the HTML unsafe to cache in any shared / edge cache (Cloudflare, Bunny, Varnish) — per RFC,Set-Cookiecombined withCache-Control: publicis unsafe. - writes an empty session row on every anonymous hit and later garbage-collects it — non-trivial write amplification under crawler / bot traffic.
The opt-out event
The controller_action_predispatch_session_start event fires in preDispatch() immediately before the FLAG_NO_START_SESSION guard. An observer can inspect the request and flip the flag to skip session start for that response.
Event payload:
| Key | Type | Description |
|---|---|---|
controller_action | Mage_Core_Controller_Varien_Action | The current controller instance |
Behavior is fully backward compatible: when no observer is registered, FLAG_NO_START_SESSION semantics are unchanged.
Example: skip session for anonymous cacheable GETs
#[Maho\Config\Observer('controller_action_predispatch_session_start')]
public function maybeSkipSession(Maho\Event\Observer $observer): void
{
$controller = $observer->getControllerAction();
$request = $controller->getRequest();
$hasSid = (bool) $request->getCookie(Mage_Core_Controller_Front_Action::SESSION_NAMESPACE);
if ($request->isGet() && !$hasSid && $this->isCacheableRoute($controller)) {
$controller->setFlag('', Mage_Core_Controller_Varien_Action::FLAG_NO_START_SESSION, true);
}
}
The observer module decides what counts as a cacheable route — typically by matching the controller's module / controller / action against a configured allowlist.
Caveat: form key on the first POST
If session start is skipped, getFormKey() returns empty for that response. The first state-changing POST from an anonymous visitor (e.g. add-to-cart) will fail _validateFormKey() and redirect — at that point the session establishes normally and subsequent requests work as usual.
This "first POST establishes session" pattern is acceptable in practice. The observing module is responsible for handling it — for example, by injecting a fresh form key client-side after the cached HTML is served, or by configuring the cache to bypass routes that submit forms.