I have a custom controller which should serve some more or less static HTML block, with this code:
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\App\Response\HttpFactory;
use Magento\Framework\App\ResponseInterface;
use Magento\Framework\Controller\ResultInterface;
use Magento\Framework\Exception\NotFoundException;
use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Result\PageFactory;
class Entries implements HttpGetActionInterface
{
public function __construct(
private Navigation $navigation,
private readonly PageFactory $resultPageFactory,
private readonly HttpFactory $httpResponseFactory
)
{
}
/**
* Execute action based on request and return result
*
* @return ResultInterface|ResponseInterface
* @throws NotFoundException
*/
public function execute()
{
$resultPage = $this->resultPageFactory->create();
$categories = $this->navigation->getNavigation(5);
$content = $resultPage->getLayout()
->createBlock(Template::class)
->setTemplate('Magento_Theme::html/header/topmenu/menu.phtml')
[...]
->toHtml();
$response = $this->httpResponseFactory->create();
$response->setContent($content);
$response->setPublicHeaders(3600);
return $response;
}
}
setPublicHeader should set a max-age and force browser caching.
$this->setHeader('pragma', 'cache', true);
$this->setHeader('cache-control', 'public, max-age=' . $ttl . ', s-maxage=' . $ttl, true);
$this->setHeader('expires', $this->getExpirationHeader('+' . $ttl . ' seconds'), true);
But it doesn't.
$ http -ph https://example.local/theme/menu/entries
HTTP/1.1 200 OK
Cache-Control: max-age=0, must-revalidate, no-cache, no-store
Is this expected behavior, when the full page cache is on? I tried to debug and in Magento the headers seem to be overwritten, but did not fully understand what's happening here with the full page cache.
EDIT:
More debugging:
\Magento\Framework\App\FrontController::processRequest calls
$this->response->setNoCacheHeaders();
When reaching
\Magento\PageCache\Model\App\FrontController\BuiltinPlugin::addDebugHeaders
Also cache control contains max-age=0
Much later \Magento\PageCache\Model\Layout\LayoutPlugin::afterGenerateElements sets the plugic header, because the layout is cacheable.
Then this code again sets no cache headers: \Magento\Framework\App\PageCache\Kernel::process
public function process(\Magento\Framework\App\Response\Http $response)
{
if (preg_match('/public.*s-maxage=(\d+)/', $response->getHeader('Cache-Control')->getFieldValue(), $matches)) {
$maxAge = $matches[1];
$response->setNoCacheHeaders(); ### trying to comment out
if (($response->getHttpResponseCode() == 200 || $response->getHttpResponseCode() == 404)
Which I don't understand, because it's actually matching for public which means "cacheable".
If I comment out this line in the above snipped, it would be cached - so literally the full page cache is disabling the browser cache.
So it boils down to this line:
Why did the inventors put that here :-) ?
1 Answer 1
Partial answer:
I now understand, why this line was put there:
s-maxage is for the shared cached, which is the FPC, see Mozilla dev docs.
Unfortunately there seems currently no simple way to
- Cache in the FPC
- and additionally cache in the browser
That really is a pity.