Content Security Policy for Marketers

Maybe you’ve already come across the term Content Security Policy (CSP) as a marketer. Here’s what you need to know about it - without getting an IT degree.

Published: 09.06.2025 | Updated: 27.11.2025 (translated)
Content: Content Security Policy (CSP), Google Tag Manager (GTM), unsafe-inline, nonce, hash, tracking issues

Admittedly, marketing and IT security rarely cross paths, but when they do, it’s often because of CSPs. And CSPs frequently clash with Google Tag Manager, which digital marketers use for everything from integrating external tools to conversion tracking for search and social campaigns.

This article explains what you, as a Digital Marketing Manager, need to know about Content Security Policies. It also covers everything required to follow my practical guide "Using Google Tag Manager with a CSP" or to troubleshoot issues when your CSP causes problems with social media pixels or external tools.


What is a CSP

A Content Security Policy is a security directive designed to prevent external content such as scripts, images or embedded elements from being executed on a website without permission. A CSP defines exactly which sources are allowed for specific content types (for example scripts, styles, media or embeds). The most important rules, at least in the context of Google Tag Manager, include...

Info: A Nonce ( «number used once» ) is a one-time random token generated when the page loads, essentially a single-use password for the script. A Hash, on the other hand, is a kind of checksum or fingerprint for your script. The hash only matches if the script’s content is unchanged, preventing manipulation.

When a user visits a webpage with a CSP, the server sends the policy to the browser via an HTTP header. The browser checks each piece of content as it loads to determine whether it is allowed. If this is not the case, the content is blocked.

Infographic CSP process

A Content Security Policy therefore mainly protects against attacks such as cross-site scripting (XSS) and similar threats. Many purely static sites work entirely without inline scripts (note that the GTM initialization script must itself be loaded inline – this special case is explained later). In such cases, neither a nonce nor a hash is required as long as all necessary scripts come from clearly defined external sources.

If you use inline scripts without defining a CSP, however, an attacker could use an insecure form to inject a script that sends user data to third parties or uses a compromised third-party script to load malware onto the user’s device.


The problem with Google Tag Manager

Perhaps you have already recognized the Gordian knot: One of the advantages of Google Tag Manager is that marketers can embed scripts or external tools on their website without editing the site’s code directly. You can adjust event tracking at any time, experiment freely and avoid depending on developers. A CSP, however, tries to prevent exactly that.

To make things more complicated, the Tag Manager’s initialization code is typically embedded as an inline script in the page’s head, which becomes a problem when inline scripts are disallowed by the CSP.

This usually results in errors in the browser console:

CSP Errors on my site

Or worse still: tracking stops working and no one knows why. The likely reason is that your GTM snippet and/or tags executed through GTM were blocked.

The dangerous shortcut: 'unsafe-inline'

Earlier, I mentioned the 'unsafe-inline' rule. At first glance, it looks promising because it allows your blocked GTM snippet to run again. However, using 'unsafe-inline' (or, worse, 'unsafe-eval', which is rarely needed in marketing contexts) is generally discouraged.

If your website handles sensitive information (login forms or other forms collecting confidential data) or processes such data in the backend, there is a good reason for this: 'unsafe-inline' makes it easier for attackers to exploit existing vulnerabilities.

Example: Your site has a form and a confirmation page where users review their entries. If the form is poorly validated and user input is not sanitized, a malicious actor could enter a script directly into a form field. When the confirmation page displays the input, the page executes the injected script and does whatever the attacker intended. If 'unsafe-inline' were disallowed, the script would not run because it is not a separate file on your server.

In short, script-src 'unsafe-inline' weakens one of the key security mechanisms of a CSP. Other attack vectors – such as unauthorized tracking pixels, 'unsafe eval' or complex, dynamically updated malware from a third-party source – are still blocked, but inline scripts remain an open door.

For commercial sites that allow user inputs, or for web applications in general, 'unsafe-inline' should therefore not be used. If your website only delivers static content and does not process user input at all, a strict CSP is usually less critical. But if your IT department has already decided that a CSP is necessary, chances are high that 'unsafe-inline' is too risky for your use case.

Info: 'unsafe-inline' is convenient but dangerous. It can be helpful in development or test environments – but in production, especially on sites with user inputs or logins, it should not be allowed for JavaScript.


And now?

You now know the essentials of Content Security Policies as a Digital Marketing Manager. If you are currently dealing with a CSP-related tracking issue, the best approach is this: Ask your developers to generate a nonce for each page load and push it into the DOM. Then follow my tutorial to configure Google Tag Manager so everything works again.

If you need help with the implementation, please get in touch - together we will find a solution:


PS: If you're a tech-savvy reader, you may already have checked my CSP and noticed that I allow 'unsafe-inline' for scripts. Guilty! For my current risk profile (static site without logins/forms) this is – as described above – an acceptable trade-off. For real web applications, however, I would not configure it this way. 😉