Guides Create account 🇬🇧 🇩🇪
  • Guides
  • Create account
  • Sign in
  • 🇬🇧 🇩🇪
  • Server recipe

    Security headers in Shopware 6

    Twenty lines in a plugin cover every page of your storefront. The part that needs care is the checkout, where a strict policy blocks the payment provider your customer is trying to pay with.

    Check my headers All security headers

    01 Where it goes

    A response subscriber, and not on the administration

    Shopware 6 is a Symfony application, so the kernel response event is the right place: it fires for every response the storefront produces, before anything reaches the browser. A small plugin with a single subscriber keeps the configuration out of your theme and lets you deactivate it in one click when a header breaks something.

    The path guard matters. The administration is a JavaScript application that relies on inline scripts and blob workers, and a policy written for the storefront will stop it loading; the API is consumed by machines that need none of these headers. Excluding the admin and api paths means a mistake in your policy costs you a broken storefront page rather than a shop you can no longer administer.

    02 The recipe

    Paste this into a plugin subscriber

    A safe starting point for a storefront. The frame-src entry is the one that is specific to a shop: PayPal, Klarna and most other providers render their payment step in an iframe on your page, and a policy limited to your own origin forbids exactly that.

    <?php declare(strict_types=1); namespace Swag\SecurityHeaders\Subscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; class SecurityHeaderSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents(): array { return [KernelEvents::RESPONSE => 'onResponse']; } public function onResponse(ResponseEvent $event): void { $path = $event->getRequest()->getPathInfo(); if (str_starts_with($path, '/admin') || str_starts_with($path, '/api')) { return; } $event->getResponse()->headers->add([             'Strict-Transport-Security' => 'max-age=63072000; includeSubDomains',
                'Content-Security-Policy' => "default-src 'self'; frame-src 'self' https://www.paypal.com; object-src 'none'; base-uri 'self'; form-action 'self' https://www.paypal.com; frame-ancestors 'self'",
                'X-Frame-Options' => 'SAMEORIGIN',
                'X-Content-Type-Options' => 'nosniff',
                'Referrer-Policy' => 'strict-origin-when-cross-origin',
                'Permissions-Policy' => 'geolocation=(), camera=(), microphone=(), payment=(self "https://www.paypal.com"), usb=()', ]); } }

    Two adjustments before this is yours. Replace the PayPal host with whatever your own payment, tracking and consent tools actually load, and get that list from Content-Security-Policy-Report-Only rather than from memory. And test it against a real order: a policy that passes on the product page can still stop the last step of a checkout.

    03 Line by line

    What each entry does

    Every entry from the block above on its own, so you can leave out what does not fit your shop rather than pasting something you cannot explain.

    Strict-Transport-Security

    What it does. Commits browsers to reaching your shop over HTTPS only, for two years. Set it at the web server if you can, so it also covers media files served without PHP, and do not add it until HTTPS works on every subdomain you use.

    'Strict-Transport-Security' => 'max-age=63072000; includeSubDomains',
    Read the full guide

    Content-Security-Policy

    What it does. Start here rather than with the enforcing header. On a shop the enforcing version blocks payment iframes, tracking pixels and consent banners, and you find that out at the moment a customer was about to pay. Report-only collects the same list without costing you the order.

    'Content-Security-Policy' => "default-src 'self'; frame-src 'self' https://www.paypal.com; object-src 'none'; base-uri 'self'; form-action 'self' https://www.paypal.com; frame-ancestors 'self'",
    'Content-Security-Policy-Report-Only' => "default-src 'self'; frame-src 'self' https://www.paypal.com; object-src 'none'; base-uri 'self'; form-action 'self' https://www.paypal.com; frame-ancestors 'self'; report-uri /csp-report",
    Read the full guide

    X-Frame-Options

    What it does. SAMEORIGIN rather than DENY, because parts of Shopware frame their own pages. This header is about your shop being embedded elsewhere; it has nothing to do with the payment iframes you embed, which frame-src governs.

    'X-Frame-Options' => 'SAMEORIGIN',
    Read the full guide

    X-Content-Type-Options

    What it does. Tells the browser to trust your declared content types rather than guessing. Safe everywhere, and on a shop that accepts supplier media it removes a whole class of upload-based attack.

    'X-Content-Type-Options' => 'nosniff',

    Referrer-Policy

    What it does. Sends the full URL within your own shop and only the bare domain to anyone else. Worth having: a full URL leaks the product, the search term and sometimes the order number to every third party you load.

    'Referrer-Policy' => 'strict-origin-when-cross-origin',
    Read the full guide

    Permissions-Policy

    What it does. Switches off camera, microphone, geolocation and USB access for your pages and anything they embed, and leaves payment to your own pages and to your payment provider. Keep geolocation off unless a store finder needs it, and remember that whatever you allow, the payment provider in your iframe is allowed too.

    'Permissions-Policy' => 'geolocation=(), camera=(), microphone=(), payment=(self "https://www.paypal.com"), usb=()',
    Read the full guide

    04 Verifying it

    Clear the caches, then read a checkout page

    Shopware caches storefront responses, and a reverse proxy such as Varnish or Fastly usually sits in front of that. A cached response was produced before your subscriber existed, so an activated plugin proves nothing on its own.

    bin/console cache:clear curl -sI https://example.com | grep -i "^strict-transport\|^content-security\|^x-frame" curl -sI https://example.com/checkout/cart | grep -i "^content-security"

    Check the cart and the checkout, not only the home page. Those are the pages that embed third parties, and they are where a policy that looked fine everywhere else stops working.

    Check my headers with a free scan

    05 Questions

    What people ask about Shopware headers

    Why not set the headers in the web server?

    You should, if the server is yours: it covers media files and error pages that never reach PHP. The subscriber is for hosting where you cannot edit the server configuration, and for the case where storefront and administration need different policies on the same domain.

    My payment method stopped working after I added the policy. What happened?

    Almost certainly frame-src, which falls back to your default-src value. The provider renders its step in an iframe from its own domain, and a policy allowing only your origin blocks it. Add the provider host to frame-src, and check connect-src as well if the widget calls home.

    Does the HTTP cache interfere with this?

    The subscriber runs on every response, including one served from the Shopware HTTP cache, so the headers are added either way. What can go stale is a page held by a proxy in front of Shopware, which stores headers along with the body. Clear that layer as well.

    Can I use a plugin from the store instead?

    Yes, and several do this properly with a settings screen. What you are buying is convenience, not better headers: the values are the same, and you take on another plugin to keep compatible across Shopware updates.

    06 In depth

    The headers in detail

    This page is the configuration. These guides are what each header actually does and how to choose its value.

    HTTP security headers

    What each response header does, which ones your site should send and how to configure them correctly.

    Read guide

    Content Security Policy

    The strongest defence against cross-site scripting: which directives to set, how to roll a policy out in report-only mode and how to reach an enforcing policy without breaking your site.

    Read guide

    HSTS

    Strict-Transport-Security explained: what max-age, includeSubDomains and preload actually do, and why the preload list is a decision you cannot quickly undo.

    Read guide

    Cookie security

    Secure, HttpOnly and SameSite: the cookie attributes that keep a session out of reach of scripts and cross-site requests, with the settings for a typical stack.

    Read guide

    Clickjacking and X-Frame-Options

    How an invisible overlay turns a visitor click into an action on your site, and the two headers that stop it: X-Frame-Options for older clients, frame-ancestors for everything else.

    Read guide

    Referrer-Policy

    Which part of your URLs travels to other sites when a visitor clicks away: the five policy values that matter, what each one gives up and the one to set by default.

    Read guide

    Permissions-Policy

    Switch off camera, microphone, geolocation and payment for your site and everything it embeds: the allow-list syntax, and why a feature you did not name is not a feature you turned off.

    Read guide

    SPF, DMARC and DNSSEC

    The records that stop someone sending mail in your name, and the one that keeps your DNS answers honest: what each does and the order to introduce them in.

    Read guide

    security.txt

    The file that tells a researcher where to report a vulnerability. Two mandatory fields, ten minutes of work, and the difference between a private report and a public one.

    Read guide

    Monitoring security headers

    A header that is right today can be gone after the next deploy. Three ways to notice: a cron job with curl, a check in your CI pipeline and a scheduled scan, with what each of them catches and misses.

    Read guide

    Detecting CSP changes

    How a policy changes quietly through deploys, plugins and CDN rules, and how to catch it: a header diff, violation reports through report-to, and what a scan comparison can and cannot show.

    Read guide

    Keep reading

    Every guide stands on its own, and together they cover what our scanner looks at. Pick the one closest to your next question.

    Is your website affected? BFSG check Accessibility statement BFSG for online shops BFSG for medical practices BFSG for hotels BFSG for tradespeople HTTP security headers Privacy signals Content Security Policy HSTS Cookie security Clickjacking and X-Frame-Options Referrer-Policy Permissions-Policy SPF, DMARC and DNSSEC security.txt Monitoring security headers Detecting CSP changes Security headers in nginx Security headers in Apache Security headers in WordPress Security headers in IIS Security headers in TYPO3 Security headers in Plesk All guides

    See which headers your shop is really sending

    Our free Quickscan reads the headers from your live response rather than from your plugin list, and explains every finding in plain language.

    Scan a website Latest scans
    Recent scans Guides Local leagues Pricing Methodology For hosters API Data protection Imprint Accessibility Terms Cancel contracts here © 2026 Erseni