Magento 2, even community edition, comes with a “full page cache” out of the box. This caching system lives near the top of Magento’s application dispatch. The frontcontroller will skip action controller dispatch if it can find a cached version of the page.
You can turn this feature off in the backend at
System -> Cache Managment -> Page Cache
The full page cache is implemented via a Magento 2 plugin on the front controller
#File: app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php
public function aroundDispatch(
MagentoFrameworkAppFrontControllerInterface $subject,
Closure $proceed,
MagentoFrameworkAppRequestInterface $request
) {
if (!$this->config->isEnabled() || $this->config->getType() != MagentoPageCacheModelConfig::BUILT_IN) {
return $proceed($request);
}
$this->version->process();
$result = $this->kernel->load();
if ($result === false) {
$result = $proceed($request);
if ($result instanceof ResponseHttp) {
$this->addDebugHeaders($result);
$this->kernel->process($result);
}
} else {
$this->addDebugHeader($result, 'X-Magento-Cache-Debug', 'HIT', true);
}
return $result;
}
The plugin’s logic is implemented in this MagentoFrameworkAppPageCacheKernel
class’s process
method
#File: lib/internal/Magento/Framework/App/PageCache/Kernel.php
public function process(MagentoFrameworkAppResponseHttp $response)
{
if (preg_match('/public.*s-maxage=(d+)/', $response->getHeader('Cache-Control')->getFieldValue(), $matches)) {
$maxAge = $matches[1];
$response->setNoCacheHeaders();
if ($response->getHttpResponseCode() == 200 && ($this->request->isGet() || $this->request->isHead())) {
$tagsHeader = $response->getHeader('X-Magento-Tags');
$tags = $tagsHeader ? explode(',', $tagsHeader->getFieldValue()) : [];
$response->clearHeader('Set-Cookie');
$response->clearHeader('X-Magento-Tags');
if (!headers_sent()) {
header_remove('Set-Cookie');
}
$this->cache->save(serialize($response), $this->identifier->getValue(), $tags, $maxAge);
}
}
}
And, finally, the Cache-Control
header that process looks for is set in another plugin.
#File: app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php
public function afterGenerateXml(MagentoFrameworkViewLayout $subject, $result)
{
if ($subject->isCacheable() && $this->config->isEnabled()) {
$this->response->setPublicHeaders($this->config->getTtl());
}
return $result;
}
Which, at the time of this writing, checks the layout object’s isCacheable
method
#File: lib/internal/Magento/Framework/View/Layout.php
public function isCacheable()
{
$this->build();
$cacheableXml = !(bool)count($this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]'));
return $this->cacheable && $cacheableXml;
}
So, what this means is
- If your request doesn’t use the layout object (i.e. JSON responses) your request skips the full page cache
- If your request does use the layout object (a normal page factory response), but there’s a single layout block with a
cacheable=false
attribute, Magento will skip full page caching.