Magento 2 Scoped CSP Module: Per-Module CSP Whitelists

Per-module CSP whitelists for Magento 2, scoped to website or store view — one integration’s source change never forces a global policy relaxation.

What leads CSP rollouts to stall on multi-vendor storefronts

This module extends Magento 2’s built-in CSP whitelist system with scope awareness. Each module or integration declares the sources it needs in a scoped_csp_whitelist.xml file, and entries can be pinned to a specific website, store group, or store view — not just applied globally. Best fit when you have or plan to enforce CSP and multiple third-party integrations with different source domains across different storefronts.

<p class="brocode-impact-callout">Typical impact: safer CSP rollouts with smaller blast radius — one integration’s source change no longer forces a policy relaxation across the entire storefront.</p>

<div class="brocode-cta-bar"><a class="wp-block-button__link wp-element-button" href="https://github.com/brosenberger/module-scoped-csp" rel="nofollow noopener" target="_blank" data-event="module_github_click">View on GitHub</a><a class="wp-block-button__link wp-element-button is-secondary" href="#brocode-inquiry" data-event="module_inquiry_click">Ask a question</a></div>

Who For

  • Magento 2 teams moving from report-only to CSP enforce mode
  • Multi-storefront sites where different websites use different analytics or payment integrations
  • Security and compliance reviewers who need a clear per-module audit trail for whitelist entries

Who Skip

  • Single-storefront sites with a small, static set of third-party scripts that rarely change
  • Teams choosing to ban inline scripts entirely via server-level CSP headers with no per-module variation

How it works

Drop a scoped_csp_whitelist.xml into your module’s etc/ folder. Entries work like the standard csp_whitelist.xml but add two optional attributes: scopeType (website, group, store, or omitted for all scopes) and scopeCode (the scope’s code). Magento merges all loaded scoped_csp_whitelist.xml files at request time and applies only the entries that match the current store context.

<!-- etc/scoped_csp_whitelist.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="urn:magento:module:BroCode_ScopedCsp:etc/scoped_csp_whitelist.xsd">
    <policies>
        <policy id="img-src">
            <values>
                <!-- Applied to all scopes (same as standard csp_whitelist.xml) -->
                <value id="cdn-global" type="host">https://cdn.example.com</value>

                <!-- Applied to website "base" only -->
                <value id="analytics-de" scopeType="website" scopeCode="base"
                    type="host">https://analytics-de.example.com</value>

                <!-- Applied to website "en" only -->
                <value id="analytics-en" scopeType="website" scopeCode="en"
                    type="host">https://analytics-en.example.com</value>
            </values>
        </policy>
    </policies>
</csp_whitelist>

If no scopeType is set, the entry applies to all scopes — identical behaviour to the native csp_whitelist.xml. Entries with a scopeType other than default require a matching scopeCode.

Before and after

Before

  • A single global CSP whitelist grows with every integration added to any storefront
  • A new source domain in one payment provider forces a policy relaxation that affects all websites
  • Audit ownership of whitelist entries is unclear — no per-module record

After

  • Each module declares only the sources it uses, scoped to the storefronts where that integration is active
  • A source change in one payment module affects only the websites that use it
  • Per-scoped_csp_whitelist.xml file gives reviewers a clear integration-level audit trail

Installation

composer require brocode/module-scoped-csp
bin/magento setup:upgrade
bin/magento cache:flush

The module enables itself automatically (active="true" in etc/module.xml). No module:enable step is needed.

Safe rollout plan

1. Run CSP in report-only mode and collect violations for at least one full traffic cycle (weekday + weekend).

2. Add scoped_csp_whitelist.xml entries per module/integration for legitimate violations only, scoped to the relevant websites.

3. Promote to enforce mode once violation noise stabilises.

4. Re-validate scope on every integration upgrade.

Rollback

Rollback is straightforward: switch CSP back to report-only mode. Scoped whitelist entries remain intact for the next iteration.

Compatibility

  • PHP 8.1, 8.2, 8.3
  • Magento 2.4.x (requires Magento_Csp)

FAQ

Will this break my storefront?

No, when rolled out with report-only first. The module is designed for incremental promotion to enforce mode.

How does it differ from the native `csp_whitelist.xml`?

The native file applies entries globally. This module adds scopeType / scopeCode attributes so entries can be pinned to a specific website, store group, or store view.

What if a third-party integration changes its source domains?

Only that integration’s scoped_csp_whitelist.xml needs updating, not the global policy. Other integrations are unaffected.

How long does a safe rollout typically take?

Most projects move from report-only to enforce within 2–4 weeks of monitoring.

What to watch out for

  • Third-party scripts loaded via tag managers can shift source domains without warning.
  • Some payment integrations rotate domains; pin only what you must.
  • Track scoped_csp_whitelist.xml changes in your module changelog so audits stay straightforward.

Take the next step

<div class="brocode-cta-bar"><a class="wp-block-button__link wp-element-button" href="https://github.com/brosenberger/module-scoped-csp" rel="nofollow noopener" target="_blank" data-event="module_github_click">View on GitHub</a><a class="wp-block-button__link wp-element-button is-secondary" href="#brocode-inquiry" data-event="module_inquiry_click">Ask a question</a></div>

Related Topics

Ask a module question or suggest improvements

Tell us your use case and the module context. Spam protection is handled by ALTCHA.




More Projects