Local Storage seems to be disabled in your browser.
For the best experience on our site, be sure to turn on Local Storage in your browser.
How does Full Page Cache(FPC) work?

Full Page Cache is one of the Magento core features that significantly boosts the speed of your store. Furthermore, this feature is available out-of-the-box. When the page cache is hit, Magento skips FrontController
and just returns the response. We are going to describe how this works on code level.
Recommended Extension
- Open Dependency
Magento 2 Free Sitemap Based Cache Warmer Extension
An Around Plugin
The core part is an around plugin for \Magento\Framework\App\FrontController::dispatch
in Magento_PageCache
module.
\Magento\PageCache\Model\App\FrontController\BuiltinPlugin::aroundDispatch
/**
* Add PageCache functionality to Dispatch method
*
* @param \Magento\Framework\App\FrontControllerInterface $subject
* @param callable $proceed
* @param \Magento\Framework\App\RequestInterface $request
* @return \Magento\Framework\Controller\ResultInterface|\Magento\Framework\App\Response\Http
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function aroundDispatch(
\Magento\Framework\App\FrontControllerInterface $subject,
\Closure $proceed,
\Magento\Framework\App\RequestInterface $request
) {
$this->version->process();
if (!$this->config->isEnabled() || $this->config->getType() !== \Magento\PageCache\Model\Config::BUILT_IN) {
return $proceed($request);
}
$result = $this->kernel->load();
if ($result === false) {
$result = $proceed($request);
if ($result instanceof ResponseHttp && !$result instanceof NotCacheableInterface) {
$this->addDebugHeaders($result);
$this->kernel->process($result);
}
} else {
$this->addDebugHeader($result, 'X-Magento-Cache-Debug', 'HIT', true);
}
return $result;
}
The Logic
$result = $this->kernel->load();
loads the full page cache.
If $result
is not false
, that is, the cache was found, this plugin will not call $proceed
, which means the whole FrontController
is skipped and the cache is used directly. The web page loads faster due to the skipping of all processing in FrontController
.
$this->addDebugHeader($result, 'X-Magento-Cache-Debug', 'HIT', true);
adds debug header in developer mode. So you can confirm if the cache is used.
$result = $this->kernel->process();
saves the full page cache if it is not available. So the next time, it will be HIT
.
Full Page Cache Saving Conditions
The conditions may not be obvious, the full page cache can only be saved if the following conditions are met.
- HTML response
HEAD
orGET
request- The response HTTP status code is
200
or404
- Public content(i.e, not customer/checkout page)
- (No block has
cacheable="false"
in layout XMLs)
X-Magento-Vary
Cookie
If you go deeper by looking at the following methods.
\Magento\PageCache\Model\App\PageCache\Kernel::process
/**
* Modify and cache application response
*
* @param \Magento\Framework\App\Response\Http $response
* @return void
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function process(\Magento\Framework\App\Response\Http $response)
{
$cacheControlHeader = $response->getHeader('Cache-Control');
if ($cacheControlHeader
&& preg_match('/public.*s-maxage=(\d+)/', $cacheControlHeader->getFieldValue(), $matches)
) {
$maxAge = $matches[1];
$response->setNoCacheHeaders();
if (($response->getHttpResponseCode() == 200 || $response->getHttpResponseCode() == 404)
&& !$response instanceof NotCacheableInterface
&& ($this->request->isGet() || $this->request->isHead())
) {
$tagsHeader = $response->getHeader('X-Magento-Tags');
$tags = $tagsHeader ? explode(',', $tagsHeader->getFieldValue() ?? '') : [];
$response->clearHeader('Set-Cookie');
if ($this->state->getMode() != AppState::MODE_DEVELOPER) {
$response->clearHeader('X-Magento-Tags');
}
$this->cookieDisabler->setCookiesDisabled(true);
$this->fullPageCache->save(
$this->serializer->serialize($this->getPreparedData($response)),
$this->identifierForSave->getValue(),
$tags,
$maxAge
);
}
}
}
\Magento\PageCache\Model\App\PageCache\Identifier::getValue
/**
* Return unique page identifier
*
* @return string
*/
public function getValue()
{
$pattern = $this->getMarketingParameterPatterns();
$replace = array_fill(0, count($pattern), '');
$url = preg_replace($pattern, $replace, (string)$this->request->getUriString());
list($baseUrl, $query) = $this->reconstructUrl($url);
$data = [
$this->request->isSecure(),
$baseUrl,
$query,
$this->request->get(\Magento\Framework\App\Response\Http::COOKIE_VARY_STRING)
?: $this->context->getVaryString()
];
return sha1($this->serializer->serialize($data));
}
See this part.
$this->request->get(\Magento\Framework\App\Response\Http::COOKIE_VARY_STRING)
?: $this->context->getVaryString()
You will notice that the full page cache identifier even depends on the X-Magento-Vary
cookie.
Closing Thoughts
To maximize your store's performance, it's recommended to use a CDN. There are free CDNs even suitable for medium size store. If you don't want to use CDN, at least, use Varnish.
If you found this blog post helpful, please share it!