Content-Security-Policy (CSP) to nagłówek HTTP, który chroni witrynę przed atakami XSS i innymi zagrożeniami. Zbyt restrykcyjna polityka potrafi jednak zablokować usługi Google — GTM, Analytics czy Maps. Ten przewodnik pokazuje, jak skonfigurować CSP tak, aby wszystkie potrzebne usługi działały.
TL;DR — gotowy nagłówek CSP dla typowej strony
Jeśli potrzebujesz od razu działającego nagłówka CSP dla witryny z GTM, GA4, Google Maps, Fonts, reCAPTCHA i YouTube, zacznij od tego i dostosuj:
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';
Zastąp RANDOM noncem generowanym per-request po stronie serwera. Zanim włączysz egzekwowanie, uruchom politykę w trybie Content-Security-Policy-Report-Only, żeby zobaczyć, co Twoja prawdziwa strona faktycznie ładuje.
Czym jest Content-Security-Policy?
CSP definiuje dozwolone źródła dla różnych typów zasobów (skrypty, style, obrazy, fonty). Przeglądarka blokuje wszystko, co nie jest jawnie dozwolone.
Podstawowa składnia
Content-Security-Policy: dyrektywa źródło1 źródło2; dyrektywa2 źródło3;
Główne dyrektywy
| Dyrektywa | Kontroluje |
|---|---|
default-src | Domyślne źródło dla wszystkich typów |
script-src | Skrypty JavaScript |
style-src | Style CSS |
img-src | Obrazy |
font-src | Fonty |
connect-src | XHR, fetch, WebSocket |
frame-src | Iframes |
object-src | Pluginy (Flash, Java) |
Wartości źródeł
| Wartość | Znaczenie |
|---|---|
'self' | Ta sama domena |
'none' | Blokuj wszystko |
'unsafe-inline' | Dozwól inline (niebezpieczne!) |
'unsafe-eval' | Dozwól eval() (niebezpieczne!) |
'nonce-xyz' | Tylko skrypty z tym nonce |
'strict-dynamic' | Zaufaj skryptom ładowanym przez zaufane |
https: | Wszystkie źródła HTTPS |
*.example.com | Subdomena |
CSP dla Google Tag Manager
GTM wymaga wielu domen i funkcjonalności. Konfiguracja zależy od trybu (client-side vs server-side).
GTM client-side — minimalna konfiguracja
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 wymaga unsafe-inline i unsafe-eval
GTM dynamicznie wstrzykuje skrypty, co wymaga 'unsafe-inline'. Tagi Custom HTML mogą używać eval(). To znacząco osłabia CSP.
Rozwiązanie 1: nonce dla GTM
<!-- Generuj nonce po stronie serwera -->
<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>
Content-Security-Policy:
script-src 'self' 'nonce-abc123' 'strict-dynamic'
https://www.googletagmanager.com;
Rozwiązanie 2: server-side GTM
Server-side GTM znacząco upraszcza CSP:
Content-Security-Policy:
script-src 'self' 'nonce-abc123'
https://gtm.twojadomena.pl;
connect-src 'self'
https://gtm.twojadomena.pl;
CSP dla Google Analytics 4
GA4 z gtag.js
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 przez GTM
Jeśli ładujesz GA4 przez GTM, potrzebujesz kombinacji powyższych reguł. Zobacz też dobre praktyki dataLayer — poprawnie zaprojektowany dataLayer pozwala ograniczyć liczbę Custom HTML tagów, co bezpośrednio upraszcza politykę CSP.
CSP dla Google Maps
Maps JavaScript API
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)
Content-Security-Policy:
frame-src
https://www.google.com
https://maps.google.com;
CSP dla Google Fonts
Content-Security-Policy:
style-src 'self'
https://fonts.googleapis.com;
font-src 'self'
https://fonts.gstatic.com;
Alternatywa: self-hosting fontów
Hostując fonty lokalnie, eliminujesz zewnętrzne zależności:
Content-Security-Policy:
font-src 'self';
style-src 'self';
CSP dla Google reCAPTCHA
reCAPTCHA v2/v3
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 dla osadzeń YouTube
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;
Pułapka 1: sam youtube-nocookie.com nie wystarczy
Wiele stron używa youtube-nocookie.com ze względu na prywatność — i dodaje do whitelisty tylko tę domenę w frame-src. To rozbija embed w kilku sytuacjach:
- Overlay z polecanymi filmami na końcu klipu przekierowuje na
youtube.com. - Linki do kanału i logo w pasku playera otwierają
youtube.com. - Część zasobów chrome playera i tak ładuje się z
youtube.comniezależnie od tego, którą domenę osadzasz.
Rozwiązanie: zawsze dodawaj do whitelisty zarówno https://www.youtube-nocookie.com, jak i https://www.youtube.com w frame-src, nawet jeśli Twój <iframe src> wskazuje tylko na wersję bez cookies.
Pułapka 2: domeny miniaturek dla lite-embeds
Jeśli używasz wzorca “facade” (klikalna miniatura zamiast pełnego iframe) — popularne rozwiązanie dla wydajności — obrazek miniatury ładuje się z innej domeny niż sam player:
https://i.ytimg.com— domyślny host miniaturek (vi/VIDEO_ID/maxresdefault.jpg).https://img.youtube.com— starszy alias, wciąż używany przez niektóre biblioteki.
Obie muszą być w img-src, inaczej podgląd po cichu się nie załaduje i użytkownik zobaczy pusty placeholder.
Pułapka 3: Cross-Origin-Opener-Policy łamie YouTube
Jeśli zabezpieczasz witrynę nagłówkiem Cross-Origin-Opener-Policy: same-origin, iframe YouTube traci dostęp do popupów, które otwiera (logowanie, udostępnianie, full-screen). Player sprawia wrażenie, że działa, ale niektóre interakcje po cichu zawodzą i nie widać żadnego błędu CSP w konsoli. Rozwiązanie: przełącz na Cross-Origin-Opener-Policy: same-origin-allow-popups.
Po pełne wyjaśnienie COOP, COEP, CORP, crossOriginIsolated oraz jak odblokować SharedArrayBuffer bez łamania integracji third-party zobacz dedykowany przewodnik: COOP, COEP, CORP — cross-origin isolation po polsku.
Gotowa produkcyjna konfiguracja
Oto dokładny fragment, którego używamy na uper.pl — łączy GTM + GA4 + osadzenia YouTube w trybie no-cookie w jednym nagłówku:
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';
W parze z Cross-Origin-Opener-Policy: same-origin-allow-popups oraz X-Frame-Options: SAMEORIGIN ta kombinacja przechodzi audyt Mozilla Observatory z oceną A+, a osadzenia YouTube działają bez zarzutu.
CSP dla Google Ads
Google Ads — śledzenie konwersji
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://www.google.pl
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
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 dla Google Consent Mode v2
Consent Mode v2 jest obowiązkowy dla ruchu z UE od marca 2024. Używa tych samych domen co GTM + GA4, ale wprowadza dwa niuanse:
- Cookiebot, Iubenda, OneTrust i inne CMP ładują się z własnych domen — dodaj do whitelisty tę, której używasz.
- Sygnały zgody są wysyłane, zanim użytkownik wyrazi zgodę (w stanie
denied), więcconnect-srcdo Google musi być otwarty od pierwszego wejścia na stronę, a nie dopiero po akceptacji.
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;
Zobacz przewodnik wdrażania Consent Mode v2 oraz jak zweryfikować, że działa poprawnie.
CSP dla Google Identity Services (GIS) / One Tap
Google Identity Services zastąpiły starą bibliotekę gapi.auth2. Logowanie Google, One Tap oraz FedCM API używają tych domen:
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;
Typowa pułapka: FedCM wymaga
frame-ancestorspo stronie Google, nie Twojej. Jeśli One Tap po cichu się nie renderuje, sprawdźaccounts.google.comw zakładce Network — błąd 403 zwykle oznacza, że Twoja strona nie znajduje się w authorized origins klienta OAuth w Google Cloud, a nie problem z CSP.
CSP dla Firebase (Auth, Firestore, Hosting)
Firebase obejmuje kilka usług Google. Każdy produkt wymaga własnego zestawu wpisów:
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 używa signInWithRedirect, który otwiera __/auth/handler na Twojej subdomenie Firebase Hosting — pamiętaj, aby dodać ją do frame-src.
Kompletna konfiguracja dla typowej witryny
Strona z GTM, GA4, Maps i Fonts
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';
Implementacja CSP
Apache (.htaccess)
Header set Content-Security-Policy "default-src 'self'; script-src 'self' https://www.googletagmanager.com; ..."
Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://www.googletagmanager.com; ..." always;
Node.js (Express)
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)
[[headers]]
for = "/*"
[headers.values]
Content-Security-Policy = "default-src 'self'; script-src 'self' https://www.googletagmanager.com; ..."
Vercel (vercel.json)
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; script-src 'self' https://www.googletagmanager.com; ..."
}
]
}
]
}
Testowanie CSP
1. Tryb Report-Only
Zacznij od trybu raportowania, który nie blokuje, a jedynie loguje naruszenia:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report;
2. DevTools przeglądarki
Konsola pokaże błędy CSP:
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/ — narzędzie Google do analizy CSP.
4. Observatory by Mozilla
https://observatory.mozilla.org/ — kompleksowy audyt bezpieczeństwa.
Raportowanie naruszeń CSP
Endpoint raportujący
Content-Security-Policy: default-src 'self'; report-uri /csp-report; report-to csp-endpoint;
Odbieranie raportów (Node.js)
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
console.log('CSP Violation:', req.body['csp-report']);
res.status(204).end();
});
Dobre praktyki
1. Zacznij restrykcyjnie
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self';
Dodawaj źródła dopiero gdy są potrzebne.
2. Unikaj unsafe-inline i unsafe-eval
Jeśli to możliwe, używaj nonce lub hash zamiast 'unsafe-inline':
<script nonce="random123">
// Twój kod
</script>
Content-Security-Policy: script-src 'nonce-random123';
3. Używaj strict-dynamic
'strict-dynamic' pozwala zaufanym skryptom ładować kolejne:
Content-Security-Policy: script-src 'nonce-abc123' 'strict-dynamic';
4. Rozważ server-side GTM
Server-side tagging drastycznie upraszcza CSP i zwiększa bezpieczeństwo.
5. Regularnie aktualizuj
Google może zmieniać domeny. Monitoruj raporty CSP i aktualizuj politykę.
Rozwiązywanie problemów — typowe błędy CSP i ich naprawa
Poniżej dokładne komunikaty, które zobaczysz w konsoli DevTools Chrome / Firefox, oraz co dodać do polityki, żeby je naprawić.
Refused to load the script 'https://www.googletagmanager.com/gtm.js'
Przyczyna: script-src nie zawiera GTM. Naprawa: dodaj https://www.googletagmanager.com do script-src.
Refused to apply inline style because it violates the following CSP directive: "style-src 'self'"
Przyczyna: GTM, Google Maps i reCAPTCHA wstrzykują style inline. Naprawa: dodaj 'unsafe-inline' do style-src lub użyj nonce, jeśli kontrolujesz wstrzykiwany kod. Hashe rzadko działają, bo style są generowane dynamicznie.
Refused to execute inline script because it violates the following CSP directive
Przyczyna: snippet GTM lub dataLayer push jest inline bez nonce. Opcje naprawy:
- Dodaj
'unsafe-inline'doscript-src(osłabia CSP). - Dodaj
nonce-xyzdo każdego inline’owego<script>ORAZ doscript-src. W połączeniu z'strict-dynamic'nonce pozwala GTM ładować kolejne tagi bez wypisywania wszystkich domen.
Refused to connect to 'https://region1.google-analytics.com/g/collect'
Przyczyna: GA4 wysyła hity na regionalne endpointy (region1, region2, …). Naprawa: użyj wildcardu: https://*.google-analytics.com w connect-src.
Refused to load the image 'https://stats.g.doubleclick.net/...'
Przyczyna: GA4 używa DoubleClick do pomiarów cross-site. Naprawa: dodaj https://stats.g.doubleclick.net do img-src i connect-src.
Refused to create a worker from 'blob:https://...'
Przyczyna: Google Maps używa Web Workers tworzonych z blob URL. Naprawa: dodaj worker-src 'self' blob: (oraz blob: do script-src, jeśli korzystasz ze starszego CSP Level 2 bez worker-src).
Refused to frame 'https://www.google.com/maps/embed'
Przyczyna: Maps Embed używa frame-src, nie img-src. Naprawa: dodaj https://www.google.com do frame-src.
reCAPTCHA pokazuje “Cannot contact reCAPTCHA”
Przyczyna: zwykle connect-src blokuje endpoint challenge’a. Naprawa: dodaj https://www.google.com/recaptcha/ do script-src i frame-src, oraz https://www.gstatic.com do script-src.
Raporty zalewają report-uri po wdrożeniu
Przyczyna: rozszerzenia przeglądarki (menedżery haseł, blokery reklam) wstrzykują skrypty i generują naruszenia CSP, z którymi nic nie zrobisz. Naprawa: filtruj raporty, w których blocked-uri zaczyna się od chrome-extension://, moz-extension:// lub safari-web-extension://, zanim uruchomią alert.
Jak bezpiecznie wdrożyć CSP
Wdrożenie strict CSP na działającej witrynie zawsze coś zepsuje — gwarantowane. Trzymaj się tej kolejności:
- Przeanalizuj obecne ładowanie — otwórz DevTools → Network → filtruj po domenie. Zbierz wszystkie third-party hosty, które faktycznie są używane. Pomocna może być też lista sposobów na sprawdzenie cookies i trackerów na stronie, żeby nie zapomnieć o domenach ładowanych dopiero po akceptacji consent.
- Wdrożenie w trybie Report-Only na co najmniej 7 dni. Produkcyjny ruch ujawni przypadki brzegowe, których nie widać na dev/staging (stare podstrony, szablony maili otwierane w przeglądarce, warianty A/B testów).
- Raporty skieruj do prawdziwego endpointu — użyj Report URI lub własnego loggera. Posortuj naruszenia według częstotliwości.
- Zaostrzaj dyrektywa po dyrektywie. Najpierw wymuszaj
img-src(niskie ryzyko), potemstyle-src,connect-src, a na końcuscript-src(największe ryzyko przestoju). - Przełącz na enforce mode z tą samą polityką. Równolegle zostaw nagłówek Report-Only do testowania kolejnej, ostrzejszej iteracji.
- Rewiduj co kwartał — Google dokłada nowe domeny (np.
analytics.google.com/g/collect, regionalneregion1/2/3.google-analytics.com). Twoja polityka musi za tym nadążać.
Sprawdź swoje CSP w czasie rzeczywistym z UPER SEO Auditor
Jeśli chcesz ciągły monitoring Content-Security-Policy bez kopiowania nagłówków do zewnętrznych walidatorów, zainstaluj naszą wtyczkę Chrome UPER SEO Auditor. Zakładka Security:
- Rozbija Twoje CSP na poszczególne dyrektywy i pokazuje każde źródło osobno — od razu widać brakujące domeny.
- Wyłapuje naruszenia CSP na żywo podczas przeglądania strony — każdy zablokowany zasób jest pogrupowany według dyrektywy, z flagą
[Google]przy blokadach dotyczących usług Google (GTM, GA4, Maps, Fonts, reCAPTCHA). - Ostrzega, gdy CSP jest w trybie Report-Only — nie wdrożysz przez pomyłkę polityki, która niczego nie egzekwuje.
- Sprawdza 10 nagłówków bezpieczeństwa poza CSP (HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy, COOP, COEP, CORP) z ważonym scorem 0-10.
Darmowa, działa lokalnie w przeglądarce — żadne dane nie opuszczają strony.
Podsumowanie
CSP dla usług Google wymaga balansu między bezpieczeństwem a funkcjonalnością:
- GTM wymaga kompromisów —
'unsafe-inline'jest często konieczny, chyba że zaadoptujesz nonce +'strict-dynamic' - Server-side GTM znacząco upraszcza CSP i eliminuje większość zewnętrznych domen skryptowych
- Nonce + strict-dynamic to aktualna dobra praktyka dla script-src
- Consent Mode v2 wymaga otwartego
connect-srcdo Google przed wyrażeniem zgody - Testuj w Report-Only przez minimum tydzień przed włączeniem wymuszania
- Monitoruj naruszenia stale — domeny Google z czasem się zmieniają
Dobrze skonfigurowana polityka CSP chroni użytkowników przed XSS, nie blokując funkcjonalności strony.
Najczęściej zadawane pytania o CSP dla usług Google
Dlaczego Google Tag Manager wymaga unsafe-inline?
GTM wstrzykuje inline'owe skrypty dla kontenera i tagów Custom HTML. Bez unsafe-inline (lub pasującego nonce) przeglądarka blokuje te wstrzyknięcia i GTM przestaje odpalać tagi. Nowoczesne rozwiązanie to nonce generowany per-request dodany do snippetu GTM oraz dyrektywa strict-dynamic, która pozwala GTM przenieść zaufanie na ładowane przez niego tagi bez wypisywania każdej domeny.
Czy można bezpiecznie używać unsafe-eval z GTM?
unsafe-eval jest potrzebny tylko, gdy Twój kontener GTM ma tagi Custom HTML lub Custom JavaScript wywołujące eval() lub new Function(). Większość tagów GA4 i Ads tego nie robi. Przejrzyj kontener w trybie Preview, usuń stare Custom HTML tam, gdzie to możliwe, a następnie wyrzuć unsafe-eval z polityki — znacząco osłabia on ochronę przed XSS.
Jak naprawić błąd "Refused to load googletagmanager.com"?
Dodaj https://www.googletagmanager.com do script-src. Jeśli dodatkowo ładujesz GA4 przez gtag.js w GTM, dodaj https://www.google-analytics.com do img-src i connect-src, a także https://*.google-analytics.com, jeśli pojawiają się błędy dla endpointów regionalnych.
Czy Google Consent Mode v2 wymaga specjalnych zmian w CSP?
Consent Mode v2 używa tych samych domen Google co GTM i GA4, więc jeśli tamte działają — Consent Mode też. Kluczowa różnica to fakt, że connect-src musi być otwarty do endpointów Google Analytics od pierwszego wejścia — Consent Mode wysyła pingi do Google w stanie "denied" jeszcze przed akceptacją cookies. Dodatkowo dodaj do whitelisty swojego dostawcę CMP (Cookiebot, Iubenda, OneTrust itp.).
Czy server-side GTM eliminuje potrzebę unsafe-inline?
Tak, i to jedna z jego największych zalet. Z server-side tagging na Twojej stronie ładuje się tylko mały first-party loader (często jeden skrypt z Twojej subdomeny), a ciężka logika tagów działa w kontenerze server-side. script-src można zredukować do self plus subdomena tagująca, z nonce — bez unsafe-inline, bez zewnętrznych domen Google w przeglądarce.
Jak testować CSP bez ryzyka dla produkcji?
Użyj nagłówka Content-Security-Policy-Report-Only. Przeglądarka przetwarza politykę i wysyła raporty naruszeń na report-uri, ale niczego nie blokuje. Uruchom tryb Report-Only przez co najmniej 7 dni na realnym ruchu, przejrzyj raporty, dociśnij politykę, a dopiero potem przełącz na wymuszający nagłówek Content-Security-Policy.
Jaka jest różnica między nonce a hash w CSP?
Nonce to losowa wartość generowana per-request i dołączana do każdego zaufanego inline'owego skryptu; przeglądarka uruchamia tylko te skrypty, których nonce pasuje do wartości w nagłówku CSP. Hash to fingerprint SHA-256 zawartości skryptu — przydatny, gdy skrypt się nie zmienia, ale kruchy, bo każda edycja go łamie. Nonce stosuj dla dynamicznej zawartości (GTM, React SSR), hashe dla statycznych snippetów inline.
Dlaczego zapytania do region1.google-analytics.com są blokowane?
GA4 wysyła hity kolekcjonera na regionalne endpointy (region1, region2, region3.google-analytics.com) zależnie od lokalizacji użytkownika. Jeśli dodałeś do whitelisty tylko www.google-analytics.com, ruch regionalny jest blokowany. Użyj wildcardu: https://*.google-analytics.com w connect-src.
Źródła
-
MDN — Content-Security-Policy https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
-
Google — CSP for Google Tag Manager https://developers.google.com/tag-platform/tag-manager/csp
-
Google — CSP Evaluator https://csp-evaluator.withgoogle.com/
-
web.dev — Content Security Policy https://web.dev/articles/csp
-
Google Maps Platform — CSP https://developers.google.com/maps/documentation/javascript/content-security-policy



