If your browser console shows errors such as "Refused to load the script … because it violates the following Content Security Policy directive", Magento's Content Security Policy (CSP) is blocking a third-party resource. Since Magento 2.4.7, CSP runs in restrict mode on checkout pages to support PCI DSS 4.0, so tools that worked before may stop loading on payment pages.

This guide shows how to allow trusted domains with csp_whitelist.xml, how to handle inline scripts properly and how Subresource Integrity (SRI) fits in.

Why Magento Uses CSP

Card-skimming attacks inject JavaScript into checkout pages that sends card details to an attacker's server. CSP tells the browser which domains may serve scripts, styles, images and connections. If an injected script tries to load from or send data to an unknown domain, the browser blocks it.

PCI DSS 4.0 requirement 6.4.3 asks merchants to authorise and maintain an inventory of scripts on payment pages, and 11.6.1 asks them to detect unauthorised changes. Magento's CSP and SRI features support both, which is why Adobe tightened the defaults.

Report-Only vs Restrict Mode

ModeBehaviour
Report-onlyViolations are logged in the browser console (and optionally sent to a report URI) but nothing is blocked.
RestrictViolations are blocked. Default for checkout pages since 2.4.7.

You can check and adjust the mode per area in config.xml. Keep checkout in restrict mode; fix violations rather than relaxing the policy.

etc/config.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <csp>
            <mode>
                <storefront_checkout_index_index>
                    <report_only>0</report_only>
                </storefront_checkout_index_index>
                <storefront>
                    <report_uri>https://your-report-endpoint.example.com/csp</report_uri>
                </storefront>
            </mode>
        </csp>
    </default>
</config>

Step 1: Read the Violation

Open the browser console on the page with the problem. Each violation names the directive (for example script-src or connect-src) and the blocked URL. A single tool often needs several directives: a script from one domain, API calls to another and images from a CDN.

Step 2: Add a csp_whitelist.xml

Create the file in your own module (never in vendor code). This example allows a session-replay tool that loads its script from one domain and sends data to another:

etc/csp_whitelist.xml
<?xml version="1.0"?>
<csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp:etc/csp_whitelist.xsd">
    <policies>
        <policy id="script-src">
            <values>
                <value id="example-analytics-cdn" type="host">cdn.example-analytics.com</value>
            </values>
        </policy>
        <policy id="connect-src">
            <values>
                <value id="example-analytics-api" type="host">api.example-analytics.com</value>
                <value id="example-analytics-ws" type="host">wss://ws.example-analytics.com</value>
            </values>
        </policy>
        <policy id="img-src">
            <values>
                <value id="example-analytics-img" type="host">img.example-analytics.com</value>
            </values>
        </policy>
    </policies>
</csp_whitelist>
bin/magento cache:flush

Whitelist exact hostnames where possible. A wildcard such as *.example.com is sometimes necessary, but every wildcard widens what an attacker could use.

Step 3: Handle Inline Scripts Properly

Inline <script> tags in templates are blocked on restrict-mode pages unless they are allowed by a hash or nonce. Magento gives templates a $secureRenderer helper that outputs inline scripts and event handlers in a CSP-compliant way:

<?php
/** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */
/** @var \Magento\Framework\Escaper $escaper */
$config = ['endpoint' => $block->getUrl('hello/greeting/lookup')];
$script = 'window.mageservicesConfig = ' . json_encode($config, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) . ';';
?>
<?= /* @noEscape */ $secureRenderer->renderTag('script', [], $script, false) ?>

Better still, move logic into a JavaScript file loaded from your theme or module, and pass configuration through data- attributes escaped with $escaper->escapeHtmlAttr().

Hash-Based Allowances

If you must allow a specific inline script that you cannot change, add its SHA-256 hash to the whitelist. The hash must match the script content exactly, so any change requires a new hash:

<policy id="script-src">
    <values>
        <value id="vendor-inline-snippet" type="hash" algorithm="sha256">BASE64_ENCODED_SHA256_HASH=</value>
    </values>
</policy>

Subresource Integrity (SRI)

From 2.4.7 Magento can generate SRI hashes for JavaScript files on payment pages during static content deployment. The browser refuses to execute a script whose content no longer matches its hash, which protects against tampered files. In 2.4.9 the hashes are stored in separate files per area, theme and locale. Always deploy static content as part of releases rather than editing files on the server, or SRI checks will fail.

CSP on Hyvä Stores

Hyvä offers a CSP-compatible theme variant, Hyva/default-csp, which uses Alpine.js's CSP build and avoids inline expressions that require unsafe-eval. If you run strict CSP on all pages, build on that variant and follow Hyvä's rules for registering inline scripts in templates.

Troubleshooting Checklist

  • Is the module containing csp_whitelist.xml enabled, and was the cache flushed?
  • Does the directive in the whitelist match the directive in the error (script-src vs connect-src)?
  • Is the hostname exact, including subdomains?
  • For WebSocket connections, did you include the wss:// scheme in connect-src?
  • Is the violation from an inline script? Use $secureRenderer or move it to a file.
  • Does the problem only happen in production? Check full-page cache and CDN caching of old headers.

Frequently Asked Questions

Should I disable Magento CSP to fix errors?

No. Disabling or switching checkout to report-only removes protection against card skimming and weakens PCI DSS 4.0 compliance. Whitelist the specific domains you trust instead.

Where do I put csp_whitelist.xml?

In the etc/ folder of one of your own modules, for example a small MageServices_Csp module that holds all third-party allowances.

Why did CSP errors appear after upgrading to 2.4.7 or later?

From 2.4.7, checkout pages run CSP in restrict mode by default, so resources that were only reported before are now blocked.

🚀

Need help with this on your store?

Our Magento engineers can implement it for you, review your code or take on the whole project.

Explore Magento Extension Development
MS
About the author

Written by the Magento Services engineering team — Magento 2, Adobe Commerce and Hyvä specialists since 2014. We regularly fix CSP violations from analytics, chat, review and session-replay tools on client stores.