CSP & block_html Cache
When unsafe-inline is excluded from the script-src CSP policy, issues can arise with blocks containing scripts cached in block_html. This also applies to cached blocks where a child block renders an inline script.
How the issue materializes depends on whether the page is cached in the full-page cache.
Uncached pages
For pages not in the full-page cache, a unique nonce attribute is injected into all registered <script> tags for each request.
When a block's output is first generated and cached in block_html, the script tag includes the correct nonce, and everything works.
However, on subsequent requests, the cached block's HTML will be used. The nonce in this cached HTML will not match the current request's nonce, preventing the script from executing.
Disabling the Full Page Cache
Disabling the full-page cache will cause invalid nonce errors from block_html cached scripts on all pages. If strict CSP is enabled site-wide, either keep the full-page cache enabled or disable both the full_page and block_html caches.
Cached pages
On pages cached in the full-page cache, the SHAs of all registered scripts are added to the CSP HTTP header.
If a block is not in the block_html cache when the page renders, the script registers, its SHA is added to the CSP header, and it works correctly.
For subsequent requests, if the page is served from the full-page cache, SHAs are returned with the content, and all remains functional.
However, if the block is already in the block_html cache before the cached page renders, the inline script won't register. Consequently, its SHA will be missing from the CSP header.
This scenario happens frequently when a cached block is used on both pages included and excluded from the full-page cache.
The only solution is to prevent scripts within blocks from being cached in block_html. The quickest way is to exclude the entire block from block_html caching, though this means its content will regenerate on every request. If this is too resource-intensive, extract the script into a separate, uncached template block. This preserves block_html's scalability benefits while resolving the issue.
What causes a block to be cached in the block_html cache?
A block is cached in block_html if its cache_lifetime data record is not false or null (a value of 0 means it never expires). This is typically set via layout XML, but can also be defined in the block class or template. For example:
<block name="example" template="Magento_Theme::block-with-a-script.phtml">
<arguments>
<argument name="cache_lifetime" xsi:type="string">3600</argument>
</arguments>
</block>
Excluding a block from the block_html cache on uncached pages
To exclude a block, set its cache_lifetime data record to false. This must be done at the same level where it was originally defined (layout XML, block class, or template).
Building on the previous example, you can deactivate block caching in a layout handle for an uncached page:
<referenceBlock name="example">
<arguments>
<argument name="cache_lifetime" xsi:type="boolean">false</argument>
</arguments>
</referenceBlock>
In PHP, determining if the page will be stored in the full page cache can be done via the layout instance using $this->layout->isCacheable().
The top-menu navigation block
The top menu navigation block is a common cached block used on all pages. For custom uncached pages in your store, ensure you exclude it from the block_html cache on those specific routes using: