---
title: "CSP for Google Services: GTM, GA4, Maps, Fonts (2026 guide)"
description: "Complete CSP configuration guide for GTM, GA4, Google Maps, Fonts, reCAPTCHA, YouTube and Ads. Ready-to-copy examples, nonce + strict-dynamic, Consent Mode v2, troubleshooting common console errors."
date: 2026-01-06
updated: 2026-04-19
category: Security
tags: ["CSP", "Content-Security-Policy", "Security", "GTM", "GA4", "Google Maps"]
url: https://uper.pl/en/blog/csp-google-services/
---

# CSP for Google Services — how to configure Content-Security-Policy for GTM, GA4, Maps, Fonts and more

**Content-Security-Policy (CSP)** is an HTTP header that protects your site against XSS attacks and other threats. However, an overly restrictive CSP can block Google services like GTM, Analytics, or Maps. This guide shows how to configure CSP so all necessary services work.

## TL;DR — ready-to-copy CSP for a typical site

If you just need a CSP header that works with GTM, GA4, Google Maps, Fonts, reCAPTCHA and YouTube, start with this and adjust:

```http
Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-RANDOM' 'strict-dynamic'
    https://www.googletagmanager.com
    https://www.google-analytics.com
    https://maps.googleapis.com
    https://www.google.com/recaptcha/
    https://www.gstatic.com/recaptcha/;
  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
  font-src 'self' https://fonts.gstatic.com;
  img-src 'self' data: blob:
    https://*.google-analytics.com
    https://*.googletagmanager.com
    https://*.gstatic.com
    https://*.googleapis.com
    https://*.ggpht.com
    https://i.ytimg.com;
  connect-src 'self'
    https://*.google-analytics.com
    https://*.analytics.google.com
    https://*.googletagmanager.com
    https://stats.g.doubleclick.net
    https://maps.googleapis.com;
  frame-src
    https://www.googletagmanager.com
    https://www.google.com
    https://www.youtube.com
    https://www.youtube-nocookie.com;
  worker-src 'self' blob:;
  object-src 'none';
  base-uri 'self';
  form-action 'self';
```

Replace `RANDOM` with a per-request nonce generated server-side. Start in `Content-Security-Policy-Report-Only` mode to see what your real site actually loads — only then switch to enforcement.

## What is Content-Security-Policy?

**CSP** defines allowed sources for different types of resources (scripts, styles, images, fonts). The browser blocks everything that is not explicitly allowed.

### Basic Syntax

```http
Content-Security-Policy: directive source1 source2; directive2 source3;
```

### Main Directives

| Directive | Controls |
|-----------|----------|
| `default-src` | Default source for all types |
| `script-src` | JavaScript scripts |
| `style-src` | CSS styles |
| `img-src` | Images |
| `font-src` | Fonts |
| `connect-src` | XHR, fetch, WebSocket |
| `frame-src` | Iframes |
| `object-src` | Plugins (Flash, Java) |

### Source Values

| Value | Meaning |
|-------|---------|
| `'self'` | Same domain |
| `'none'` | Block everything |
| `'unsafe-inline'` | Allow inline (dangerous!) |
| `'unsafe-eval'` | Allow eval() (dangerous!) |
| `'nonce-xyz'` | Only scripts with this nonce |
| `'strict-dynamic'` | Trust scripts loaded by trusted ones |
| `https:` | All HTTPS sources |
| `*.example.com` | Subdomain |

## CSP for Google Tag Manager

GTM requires many domains and functionalities. Configuration depends on mode (client-side vs server-side).

### GTM Client-Side - Minimal Configuration

```http
Content-Security-Policy:
  script-src 'self' 'unsafe-inline' 'unsafe-eval'
    https://www.googletagmanager.com
    https://tagmanager.google.com;
  img-src 'self' data:
    https://www.googletagmanager.com
    https://www.google-analytics.com
    https://ssl.gstatic.com
    https://www.gstatic.com;
  connect-src 'self'
    https://www.google-analytics.com
    https://analytics.google.com
    https://stats.g.doubleclick.net
    https://region1.google-analytics.com;
  frame-src
    https://www.googletagmanager.com;
```

### Problem: GTM Requires unsafe-inline and unsafe-eval

GTM dynamically injects scripts, which requires `'unsafe-inline'`. Custom HTML tags may use `eval()`. This significantly weakens CSP.

### Solution 1: Nonce for GTM

```html
<!-- Generate nonce server-side -->
<script nonce="abc123">
  (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;
  j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;
  j.nonce='abc123';
  f.parentNode.insertBefore(j,f);
  })(window,document,'script','dataLayer','GTM-XXXXX');
</script>
```

```http
Content-Security-Policy:
  script-src 'self' 'nonce-abc123' 'strict-dynamic'
    https://www.googletagmanager.com;
```

### Solution 2: Server-Side GTM

[Server-side GTM](/en/blog/gtm-server-side-vs-client-side/) significantly simplifies CSP:

```http
Content-Security-Policy:
  script-src 'self' 'nonce-abc123'
    https://gtm.yourdomain.com;
  connect-src 'self'
    https://gtm.yourdomain.com;
```

## CSP for Google Analytics 4

### GA4 with gtag.js

```http
Content-Security-Policy:
  script-src 'self'
    https://www.googletagmanager.com;
  img-src 'self'
    https://www.google-analytics.com
    https://www.googletagmanager.com;
  connect-src 'self'
    https://www.google-analytics.com
    https://analytics.google.com
    https://region1.google-analytics.com
    https://region2.google-analytics.com
    https://region3.google-analytics.com;
```

### GA4 with GTM

If you load GA4 through GTM, you need a combination of the above. See also [dataLayer best practices](/en/blog/datalayer-best-practices/) — a well-designed dataLayer lets you reduce the number of Custom HTML tags, which directly simplifies your CSP policy.

## CSP for Google Maps

### Maps JavaScript API

```http
Content-Security-Policy:
  script-src 'self' 'unsafe-inline'
    https://maps.googleapis.com
    https://maps.gstatic.com;
  img-src 'self' data: blob:
    https://maps.googleapis.com
    https://maps.gstatic.com
    https://*.ggpht.com
    https://*.google.com
    https://*.googleapis.com;
  style-src 'self' 'unsafe-inline'
    https://fonts.googleapis.com;
  font-src 'self'
    https://fonts.gstatic.com;
  connect-src 'self'
    https://maps.googleapis.com
    https://places.googleapis.com;
  frame-src
    https://www.google.com;
  worker-src blob:;
```

### Maps Embed API (iframe)

```http
Content-Security-Policy:
  frame-src
    https://www.google.com
    https://maps.google.com;
```

## CSP for Google Fonts

```http
Content-Security-Policy:
  style-src 'self'
    https://fonts.googleapis.com;
  font-src 'self'
    https://fonts.gstatic.com;
```

### Alternative: Self-hosting Fonts

Hosting fonts locally eliminates external dependencies:

```http
Content-Security-Policy:
  font-src 'self';
  style-src 'self';
```

## CSP for Google reCAPTCHA

### reCAPTCHA v2/v3

```http
Content-Security-Policy:
  script-src 'self'
    https://www.google.com/recaptcha/
    https://www.gstatic.com/recaptcha/;
  frame-src
    https://www.google.com/recaptcha/
    https://recaptcha.google.com;
  style-src 'self' 'unsafe-inline';
```

## CSP for YouTube Embeds

```http
Content-Security-Policy:
  frame-src
    https://www.youtube.com
    https://www.youtube-nocookie.com;
  img-src 'self'
    https://i.ytimg.com
    https://img.youtube.com;
```

### Gotcha 1: youtube-nocookie.com alone isn't enough

Many sites use `youtube-nocookie.com` for privacy reasons — and whitelist **only** that domain in `frame-src`. That breaks the embed in several scenarios:

- **Related videos overlay** at the end of a clip redirects to `youtube.com`.
- **Channel links and logo** in the player bar open `youtube.com`.
- **Some player chrome assets** are still served from `youtube.com` regardless of which domain you embed.

**Fix:** always whitelist both `https://www.youtube-nocookie.com` **and** `https://www.youtube.com` in `frame-src`, even if your `<iframe src>` points only to the no-cookie version.

### Gotcha 2: thumbnail domains for lite embeds

If you use a "facade" pattern (click-to-play thumbnail instead of a full iframe) — popular for performance — the thumbnail image loads from a different domain than the player:

- `https://i.ytimg.com` — default thumbnail host (`vi/VIDEO_ID/maxresdefault.jpg`).
- `https://img.youtube.com` — legacy alias, still used by some libraries.

Both need to be in `img-src`, otherwise the preview silently fails and users see a blank placeholder.

### Gotcha 3: Cross-Origin-Opener-Policy breaking YouTube

If you harden your site with `Cross-Origin-Opener-Policy: same-origin`, the YouTube iframe loses access to popups it opens (sign-in, sharing dialog, full-screen). The player appears to work, but certain interactions fail silently with no CSP error in the console. **Fix:** switch to `Cross-Origin-Opener-Policy: same-origin-allow-popups`.

For a full breakdown of COOP, COEP, CORP, `crossOriginIsolated`, and how to unlock `SharedArrayBuffer` without breaking third-party integrations, see the dedicated guide: [COOP, COEP, CORP — cross-origin isolation explained](/en/blog/coop-coep-corp-cross-origin-isolation/).

### Production-ready example

Here is the exact snippet we use on uper.pl, combining GTM + GA4 + YouTube no-cookie embeds in a single header:

```http
Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'unsafe-inline' 'unsafe-eval'
    https://www.googletagmanager.com
    https://tagmanager.google.com;
  style-src 'self' 'unsafe-inline'
    https://cdn.jsdelivr.net;
  font-src 'self'
    https://cdn.jsdelivr.net;
  img-src 'self' data:
    https://www.googletagmanager.com
    https://www.google-analytics.com
    https://ssl.gstatic.com
    https://www.gstatic.com
    https://i.ytimg.com;
  connect-src 'self'
    https://www.google-analytics.com
    https://analytics.google.com
    https://stats.g.doubleclick.net
    https://*.google-analytics.com
    https://*.analytics.google.com
    https://*.googletagmanager.com;
  frame-src
    https://www.googletagmanager.com
    https://www.youtube-nocookie.com
    https://www.youtube.com;
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'self';
```

Paired with `Cross-Origin-Opener-Policy: same-origin-allow-popups` and `X-Frame-Options: SAMEORIGIN`, this combination passes Mozilla Observatory with an A+ and still lets YouTube embeds work end-to-end.

## CSP for Google Ads

### Google Ads Conversion Tracking

```http
Content-Security-Policy:
  script-src 'self' 'unsafe-inline'
    https://www.googleadservices.com
    https://www.googletagmanager.com
    https://googleads.g.doubleclick.net;
  img-src 'self'
    https://www.google.com
    https://googleads.g.doubleclick.net
    https://www.googleadservices.com;
  connect-src 'self'
    https://www.google.com
    https://pagead2.googlesyndication.com;
  frame-src
    https://bid.g.doubleclick.net
    https://tpc.googlesyndication.com;
```

### Google AdSense

```http
Content-Security-Policy:
  script-src 'self' 'unsafe-inline' 'unsafe-eval'
    https://pagead2.googlesyndication.com
    https://adservice.google.com
    https://www.googletagservices.com
    https://partner.googleadservices.com;
  img-src 'self' data:
    https://pagead2.googlesyndication.com
    https://tpc.googlesyndication.com
    https://*.google.com;
  frame-src
    https://googleads.g.doubleclick.net
    https://tpc.googlesyndication.com
    https://www.google.com;
  style-src 'self' 'unsafe-inline';
```

## CSP for Google Consent Mode v2

**Consent Mode v2** is mandatory for EU traffic since March 2024. It uses the same domains as GTM + GA4, but introduces two wrinkles you need to account for:

1. **Cookiebot, Iubenda, OneTrust and other CMPs** each load from their own domain — whitelist whichever one you use.
2. **Consent signals are sent before consent is granted** (in `denied` state), so `connect-src` to Google must be open from the first pageview, not after opt-in.

```http
Content-Security-Policy:
  script-src 'self' 'nonce-abc123'
    https://www.googletagmanager.com
    https://consent.cookiebot.com
    https://consentcdn.cookiebot.com;
  connect-src 'self'
    https://consent.cookiebot.com
    https://consentcdn.cookiebot.com
    https://www.google-analytics.com
    https://region1.google-analytics.com;
  frame-src
    https://consent.cookiebot.com
    https://consentcdn.cookiebot.com;
```

See the [Consent Mode v2 implementation guide](/en/blog/google-consent-mode-v2/) and [how to verify it's working](/en/blog/verify-google-consent-mode-v2/).

## CSP for Google Identity Services (GIS) / One Tap

Google Identity Services replaced the old `gapi.auth2` library. Sign-in with Google, One Tap, and the FedCM API all use these domains:

```http
Content-Security-Policy:
  script-src 'self'
    https://accounts.google.com/gsi/client;
  connect-src 'self'
    https://accounts.google.com/gsi/
    https://accounts.google.com/.well-known/;
  frame-src
    https://accounts.google.com/gsi/
    https://accounts.google.com/o/oauth2/;
  style-src 'self' 'unsafe-inline'
    https://accounts.google.com/gsi/style;
```

> **Common gotcha:** FedCM requires `frame-ancestors` on Google's side, not yours. If One Tap silently fails to render, check `accounts.google.com` in Network tab — a 403 usually means your site is missing from the Google Cloud OAuth client's authorized origins, not a CSP issue.

## CSP for Firebase (Auth, Firestore, Hosting)

Firebase spans several Google properties. Each product needs its own set of entries:

```http
Content-Security-Policy:
  script-src 'self'
    https://www.gstatic.com/firebasejs/
    https://apis.google.com
    https://www.googleapis.com;
  connect-src 'self'
    https://*.firebaseio.com
    https://*.firebaseapp.com
    https://firestore.googleapis.com
    https://identitytoolkit.googleapis.com
    https://securetoken.googleapis.com
    wss://*.firebaseio.com;
  frame-src
    https://*.firebaseapp.com;
```

Firebase Auth specifically uses `signInWithRedirect` which opens `__/auth/handler` on your Firebase Hosting subdomain — make sure to allow it in `frame-src`.

## Complete Configuration for a Typical Site

### Site with GTM, GA4, Maps, and Fonts

```http
Content-Security-Policy:
  default-src 'self';

  script-src 'self' 'unsafe-inline' 'unsafe-eval'
    https://www.googletagmanager.com
    https://tagmanager.google.com
    https://www.google-analytics.com
    https://maps.googleapis.com
    https://maps.gstatic.com;

  style-src 'self' 'unsafe-inline'
    https://fonts.googleapis.com
    https://tagmanager.google.com;

  img-src 'self' data: blob:
    https://www.googletagmanager.com
    https://www.google-analytics.com
    https://ssl.gstatic.com
    https://www.gstatic.com
    https://maps.googleapis.com
    https://maps.gstatic.com
    https://*.ggpht.com
    https://*.google.com;

  font-src 'self'
    https://fonts.gstatic.com;

  connect-src 'self'
    https://www.google-analytics.com
    https://analytics.google.com
    https://region1.google-analytics.com
    https://stats.g.doubleclick.net
    https://maps.googleapis.com;

  frame-src
    https://www.googletagmanager.com
    https://www.google.com
    https://www.youtube.com;

  object-src 'none';
  base-uri 'self';
  form-action 'self';
```

## Implementing CSP

### Apache (.htaccess)

```apache
Header set Content-Security-Policy "default-src 'self'; script-src 'self' https://www.googletagmanager.com; ..."
```

### Nginx

```nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://www.googletagmanager.com; ..." always;
```

### Node.js (Express)

```javascript
const helmet = require('helmet');

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", "https://www.googletagmanager.com"],
    styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
    imgSrc: ["'self'", "data:", "https://www.google-analytics.com"],
    fontSrc: ["'self'", "https://fonts.gstatic.com"],
    connectSrc: ["'self'", "https://www.google-analytics.com"],
    frameSrc: ["https://www.googletagmanager.com"]
  }
}));
```

### Netlify (netlify.toml)

```toml
[[headers]]
  for = "/*"
  [headers.values]
    Content-Security-Policy = "default-src 'self'; script-src 'self' https://www.googletagmanager.com; ..."
```

### Vercel (vercel.json)

```json
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Content-Security-Policy",
          "value": "default-src 'self'; script-src 'self' https://www.googletagmanager.com; ..."
        }
      ]
    }
  ]
}
```

## Testing CSP

### 1. Report-Only Mode

Start with reporting mode, which doesn't block but only logs:

```http
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report;
```

### 2. Browser DevTools

Console will show CSP errors:
```
Refused to load the script 'https://example.com/script.js' because it violates the Content Security Policy directive: "script-src 'self'".
```

### 3. CSP Evaluator

[https://csp-evaluator.withgoogle.com/](https://csp-evaluator.withgoogle.com/) - Google's tool for CSP analysis.

### 4. Observatory by Mozilla

[https://observatory.mozilla.org/](https://observatory.mozilla.org/) - comprehensive security audit.

## CSP Violation Reporting

### Reporting Endpoint

```http
Content-Security-Policy: default-src 'self'; report-uri /csp-report; report-to csp-endpoint;
```

### Receiving Reports (Node.js)

```javascript
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
  console.log('CSP Violation:', req.body['csp-report']);
  res.status(204).end();
});
```

## Best Practices

### 1. Start Restrictively

```http
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self';
```

Add sources only when needed.

### 2. Avoid unsafe-inline and unsafe-eval

If possible, use nonce or hash instead of `'unsafe-inline'`:

```html
<script nonce="random123">
  // Your code
</script>
```

```http
Content-Security-Policy: script-src 'nonce-random123';
```

### 3. Use strict-dynamic

`'strict-dynamic'` allows trusted scripts to load subsequent ones:

```http
Content-Security-Policy: script-src 'nonce-abc123' 'strict-dynamic';
```

### 4. Consider Server-Side GTM

[Server-side tagging](/en/blog/gtm-server-side-vs-client-side/) drastically simplifies CSP and increases security.

### 5. Update Regularly

Google may change domains. Monitor CSP reports and update the policy.

## Troubleshooting — common CSP errors and fixes

Below are the exact console messages you'll see in Chrome / Firefox DevTools, and what to add to your policy to fix each one.

### `Refused to load the script 'https://www.googletagmanager.com/gtm.js'`

**Cause:** `script-src` doesn't include GTM. **Fix:** add `https://www.googletagmanager.com` to `script-src`.

### `Refused to apply inline style because it violates the following CSP directive: "style-src 'self'"`

**Cause:** GTM, Google Maps, and reCAPTCHA all inject inline styles. **Fix:** add `'unsafe-inline'` to `style-src`, or use a nonce if you control the injected markup. Hashes rarely work here because styles are generated dynamically.

### `Refused to execute inline script because it violates the following CSP directive`

**Cause:** your GTM snippet or dataLayer push is inline without a nonce. **Fix options:**

- Add `'unsafe-inline'` to `script-src` (weakens CSP).
- Add `nonce-xyz` to every inline `<script>` tag AND to `script-src`. Paired with `'strict-dynamic'`, nonce lets GTM load downstream tags without listing every domain.

### `Refused to connect to 'https://region1.google-analytics.com/g/collect'`

**Cause:** GA4 sends hits to region-specific endpoints (`region1`, `region2`, …). **Fix:** use a wildcard: `https://*.google-analytics.com` in `connect-src`.

### `Refused to load the image 'https://stats.g.doubleclick.net/...'`

**Cause:** GA4 uses DoubleClick for cross-site measurement. **Fix:** add `https://stats.g.doubleclick.net` to both `img-src` and `connect-src`.

### `Refused to create a worker from 'blob:https://...'`

**Cause:** Google Maps uses Web Workers created from blob URLs. **Fix:** add `worker-src 'self' blob:` (and `blob:` to `script-src` if you're on an older CSP Level 2 without `worker-src`).

### `Refused to frame 'https://www.google.com/maps/embed'`

**Cause:** Maps Embed uses `frame-src`, not `img-src`. **Fix:** add `https://www.google.com` to `frame-src`.

### reCAPTCHA shows "Cannot contact reCAPTCHA"

**Cause:** usually `connect-src` is blocking the challenge endpoint. **Fix:** add `https://www.google.com/recaptcha/` to both `script-src` and `frame-src`, and `https://www.gstatic.com` to `script-src`.

### Reports flood to `report-uri` after deploy

**Cause:** browser extensions (password managers, ad blockers) inject scripts and trigger CSP violations you can't do anything about. **Fix:** filter reports where `blocked-uri` starts with `chrome-extension://`, `moz-extension://`, or `safari-web-extension://` before alerting.

## How to roll out CSP safely

Deploying a strict CSP on an existing site will break something — guaranteed. Follow this sequence:

1. **Audit current load** — open DevTools → Network → filter by domain. Collect every third-party host actually used. Our [guide to checking cookies and trackers on a page](/en/blog/check-cookies-and-trackers/) is useful here so you don't miss domains that only load after consent is granted.
2. **Deploy in Report-Only mode** for at least 7 days. Production traffic will reveal edge cases dev/staging misses (legacy pages, email templates opened in-browser, A/B test variants).
3. **Route reports to a real endpoint** — use [Report URI](https://report-uri.com/) or your own logger. Triage violations by frequency.
4. **Tighten directive by directive.** Enforce `img-src` first (low risk), then `style-src`, `connect-src`, and finally `script-src` (highest breakage risk).
5. **Switch to enforce mode** with the same policy. Keep the Report-Only header in parallel when testing a stricter next iteration.
6. **Revisit quarterly** — Google adds domains (e.g. `analytics.google.com/g/collect`, regional `region1/2/3.google-analytics.com`). Your policy needs to follow.

## Check your CSP in real time with UPER SEO Auditor

If you want an ongoing sanity check on your Content-Security-Policy — without copy-pasting headers into online validators — install our Chrome extension **[UPER SEO Auditor](https://chromewebstore.google.com/detail/uper-seo-auditor/khhpbeckpphaoiemjdijhbfpjnendage)**. The Security tab:

- **Parses your CSP** into individual directives and shows every source side-by-side, so you can spot missing domains at a glance.
- **Captures live CSP violations** as you browse — every blocked resource is grouped by violated directive, with a **`[Google]` flag on blocks affecting Google services** (GTM, GA4, Maps, Fonts, reCAPTCHA).
- **Warns if CSP is in Report-Only mode** so you don't deploy a weakened policy to production by mistake.
- **Checks 10 security headers** beyond CSP (HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy, COOP, COEP, CORP) with a weighted 0-10 score.

It's free and runs entirely in your browser — no data leaves the page.

## Summary

**CSP for Google Services** requires balancing security and functionality:

1. **GTM requires compromises** — `'unsafe-inline'` is often necessary unless you adopt nonce + `'strict-dynamic'`
2. **Server-side GTM** significantly simplifies CSP and removes most third-party script domains
3. **Nonce + strict-dynamic** is the current best practice for script-src
4. **Consent Mode v2** needs `connect-src` open to Google *before* consent is granted
5. **Test in Report-Only** for at least a week before enforcing
6. **Monitor violations** continuously — Google domains drift over time

A well-configured CSP protects users against XSS without blocking site functionality.

<FaqBlog
  questions={[
    {
      question: 'Why does Google Tag Manager require unsafe-inline?',
      answer: 'GTM injects inline scripts for its container and Custom HTML tags. Without <code>unsafe-inline</code> (or a matching nonce), the browser blocks those injections and GTM stops firing tags. The modern fix is to add a per-request nonce to the GTM snippet and use <code>strict-dynamic</code>, which lets GTM propagate trust to tags it loads without listing every domain.'
    },
    {
      question: 'Is it safe to use unsafe-eval with GTM?',
      answer: '<code>unsafe-eval</code> is only needed if your GTM container uses Custom HTML or Custom JavaScript tags that call <code>eval()</code> or <code>new Function()</code>. Most GA4 and Ads tags do not. Audit your container in Preview mode, remove legacy Custom HTML where possible, and drop <code>unsafe-eval</code> from your policy — it materially weakens XSS protection.'
    },
    {
      question: 'How do I fix "Refused to load googletagmanager.com" errors?',
      answer: 'Add <code>https://www.googletagmanager.com</code> to <code>script-src</code>. If you also load GA4 via gtag.js through GTM, add <code>https://www.google-analytics.com</code> to <code>img-src</code> and <code>connect-src</code>, and <code>https://*.google-analytics.com</code> if you see regional endpoint errors.'
    },
    {
      question: 'Does Google Consent Mode v2 need special CSP changes?',
      answer: 'Consent Mode v2 uses the same Google domains as GTM and GA4, so if those work, Consent Mode works. The key change is that <code>connect-src</code> must be open to Google analytics endpoints from the first pageview — Consent Mode pings Google in "denied" state before the user accepts cookies. Additionally, whitelist your CMP provider (Cookiebot, Iubenda, OneTrust, etc.).'
    },
    {
      question: 'Can server-side GTM eliminate the need for unsafe-inline?',
      answer: 'Yes, and that is one of its biggest advantages. With server-side tagging, only a small first-party loader runs on your site (often just one script from your own subdomain), and the heavy tag logic runs on your server container. Your <code>script-src</code> can be reduced to <code>self</code> plus your tagging subdomain, with a nonce — no <code>unsafe-inline</code>, no third-party Google domains in the browser.'
    },
    {
      question: 'How do I test CSP without breaking my production site?',
      answer: 'Use the <code>Content-Security-Policy-Report-Only</code> header. The browser processes your policy and sends violation reports to a <code>report-uri</code> endpoint, but never blocks anything. Run it in Report-Only for at least 7 days against real traffic, review reports, tighten the policy, then switch to the enforcing <code>Content-Security-Policy</code> header.'
    },
    {
      question: 'What is the difference between nonce and hash in CSP?',
      answer: 'A <strong>nonce</strong> is a random value generated per request and attached to each trusted inline script tag; the browser only executes scripts whose nonce matches the one in the CSP header. A <strong>hash</strong> is a SHA-256 fingerprint of the script contents — useful when the script never changes, but brittle because any edit breaks it. Use nonces for dynamic content (GTM, React SSR) and hashes for static inline snippets.'
    },
    {
      question: 'Why are region1.google-analytics.com requests blocked?',
      answer: 'GA4 routes collection hits to regional endpoints (<code>region1</code>, <code>region2</code>, <code>region3</code>.google-analytics.com) depending on user location. If you only whitelisted <code>www.google-analytics.com</code>, regional traffic is blocked. Use a wildcard: <code>https://*.google-analytics.com</code> in <code>connect-src</code>.'
    }
  ]}
  heading="Frequently Asked Questions about CSP for Google Services"
  id="faq"
/>

## Sources

1. **MDN - Content-Security-Policy**
[https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)

2. **Google - CSP for Google Tag Manager**
[https://developers.google.com/tag-platform/tag-manager/csp](https://developers.google.com/tag-platform/tag-manager/csp)

3. **Google - CSP Evaluator**
[https://csp-evaluator.withgoogle.com/](https://csp-evaluator.withgoogle.com/)

4. **web.dev - Content Security Policy**
[https://web.dev/articles/csp](https://web.dev/articles/csp)

5. **Google Maps Platform - CSP**
[https://developers.google.com/maps/documentation/javascript/content-security-policy](https://developers.google.com/maps/documentation/javascript/content-security-policy)
