Jak włączyć nagłówki bezpieczeństwa (Security Headers)

dowiedz się

Bezpieczne serwowanie aplikacji webowej zaczyna się od właściwych nagłówków HTTP. To szybka, tania i skuteczna bariera przed clickjackingiem, wyciekiem refererów, wykonaniem niechcianego skryptu czy mieszaniem treści HTTP/HTTPS. W tej instrukcji krok po kroku włączysz kluczowe nagłówki, przygotujesz bezpieczną politykę zasobów, wdrożysz ją na popularnych serwerach (Nginx, Apache, IIS, Node) oraz sprawdzisz wynik narzędziami testującymi. Zadbamy też o stopniowe egzekwowanie i monitorowanie błędów.

Co włączyć i w jakiej kolejności

Najważniejsze nagłówki i ich rola

Pełną listę warto budować warstwowo. Oto fundamenty, które powinieneś wdrożyć w każdym projekcie:

  • Content-Security-Policy (CSP) – kontroluje, skąd można ładować skrypty, style, obrazy, fonty czy ramki. Pierwszy etap to tryb obserwacyjny (Report-Only), a docelowo egzekwowanie. W tym tekście użyjemy skrótu Content-Security-Policy w roli kluczowego pojęcia.
  • Strict-Transport-Security – wymusza HTTPS po pierwszej wizycie. Skrótowo będziemy mówić HSTS. Można go zgłosić do preloadera przeglądarek.
  • X-Frame-Options – blokuje osadzanie strony w ramkach (zapobieganie clickjackingowi). Wyróżniamy X-Frame-Options z wartościami DENY/SAMEORIGIN.
  • X-Content-Type-Options – zapobiega sniffingowi typów MIME. Flaga X-Content-Type-Options powinna być ustawiona na nosniff.
  • Referrer-Policy – ogranicza przekazywanie adresu odsyłającego. Zalecane np. strict-origin-when-cross-origin. W dalszej części oznaczamy jako Referrer-Policy.
  • Permissions-Policy – ogranicza dostęp do API przeglądarki (kamera, mikrofon, geolokalizacja itd.). W tekście stosujemy pojęcie Permissions-Policy.
  • Cross-Origin-Opener-Policy – izolacja kontekstu okna względem zewnętrznych źródeł, przydatne z COEP/COEP. Wyróżniamy COOP.
  • Cross-Origin-Embedder-Policy – kontrola osadzanych zasobów spoza originu, ważna dla SharedArrayBuffer i izolacji. Oznaczamy COEP.
  • Cross-Origin-Resource-Policy – ogranicza, kto może używać twoich zasobów. Skrótowo CORP.
  • Cross-Origin Resource Sharing – mechanizm udostępniania zasobów między originami; nie jest to nagłówek “bezpieczeństwa” per se, ale bywa współkonfigurowany. Będziemy mówić o CORS przy integracjach.

Dodatkowo rozważ: X-Permitted-Cross-Domain-Policies (Flash/Adobe – dziś historyczne, ale wciąż spotykane), Clear-Site-Data (czyszczenie storage), Report-To/Reporting-Endpoints (zbieranie raportów), atrybuty COOP/COEP preload. Nagłówki X-XSS-Protection i Expect-CT są przestarzałe – zazwyczaj nie włączaj ich, chyba że wymaga tego starsze środowisko.

Praktyczna kolejność wdrożenia

  • Etap 1 (niski ryzyk): HSTS (bez preloadu), X-Frame-Options, X-Content-Type-Options, Referrer-Policy.
  • Etap 2 (średni ryzyk): Permissions-Policy, CORP, COOP, COEP – początkowo w konfiguracjach najmniej restrykcyjnych.
  • Etap 3 (wysoki wpływ): Content-Security-Policy w trybie Report-Only, zbieranie raportów, następnie egzekwowanie i uszczelnianie.
  • Opcjonalnie: rejestracja HSTS Preload, Reporting-Endpoints, Clear-Site-Data przy migracjach.

Wartości bezpieczne na start

  • X-Frame-Options: DENY (lub SAMEORIGIN, jeśli świadomie osadzasz wewnątrz iframów).
  • X-Content-Type-Options: nosniff.
  • Referrer-Policy: strict-origin-when-cross-origin (dobry kompromis między użytecznością a prywatnością).
  • Permissions-Policy: stopniowo, np. geolocation=(), microphone=(), camera=().
  • CORP: same-origin (dla wrażliwych zasobów), lub same-site gdy potrzebujesz współdzielić w obrębie tej samej strony.
  • COOP: same-origin.
  • COEP: require-corp.
  • HSTS: max-age=31536000; includeSubDomains (po audycie), preload (po dłuższym okresie stabilności).
  • CSP (start): default-src 'self’; object-src 'none’; base-uri 'self’; frame-ancestors 'none’.

Konfiguracja na popularnych serwerach

Nginx

1) Otwórz główną konfigurację serwera lub blok server: /etc/nginx/nginx.conf albo /etc/nginx/sites-available/example.conf.

2) W bloku server dla HTTPS dodaj przykładowe nagłówki:

  • add_header Strict-Transport-Security „max-age=31536000” always;
  • add_header X-Frame-Options „DENY” always;
  • add_header X-Content-Type-Options „nosniff” always;
  • add_header Referrer-Policy „strict-origin-when-cross-origin” always;
  • add_header Permissions-Policy „geolocation=(), microphone=(), camera=()” always;
  • add_header Cross-Origin-Opener-Policy „same-origin” always;
  • add_header Cross-Origin-Embedder-Policy „require-corp” always;
  • add_header Cross-Origin-Resource-Policy „same-origin” always;
  • add_header Content-Security-Policy „default-src 'self’; object-src 'none’; base-uri 'self’; frame-ancestors 'none'” always;

3) Zrestartuj serwer: systemctl reload nginx. Uwaga: always gwarantuje nagłówek także dla odpowiedzi 4xx/5xx i przy przekierowaniach.

4) CSP z nonce/hash. Dla dynamicznych skryptów wygodną ścieżką jest nonce. W Nginx możesz wygenerować nonce zmienną map i przekazać do upstreamu albo użyć modułów trzecich. Popularna praktyka: aplikacja generuje nonce i zwraca go w odpowiedzi, a Nginx wstawia go do CSP za pomocą add_header z wartościami wstawianymi przez sub_filter lub bardziej zaawansowaną logiką w aplikacji (zalecane).

5) HSTS Preload: dopiero po kilku tygodniach stabilności dodaj includeSubDomains; preload i zgłoś domenę do hstspreload.org.

Apache HTTPD / .htaccess

1) Upewnij się, że działa moduł headers: a2enmod headers, a następnie systemctl reload apache2.

2) W pliku VirtualHost dla :443 lub w .htaccess dodaj:

  • Header always set Strict-Transport-Security „max-age=31536000”
  • Header always set X-Frame-Options „DENY”
  • Header always set X-Content-Type-Options „nosniff”
  • Header always set Referrer-Policy „strict-origin-when-cross-origin”
  • Header always set Permissions-Policy „geolocation=(), microphone=(), camera=()”
  • Header always set Cross-Origin-Opener-Policy „same-origin”
  • Header always set Cross-Origin-Embedder-Policy „require-corp”
  • Header always set Cross-Origin-Resource-Policy „same-origin”
  • Header always set Content-Security-Policy „default-src 'self’; object-src 'none’; base-uri 'self’; frame-ancestors 'none'”

3) Jeżeli używasz .htaccess, pamiętaj o AllowOverride All w konfiguracji katalogu. Lepiej jednak ustawiać nagłówki w VirtualHost niż w .htaccess ze względów wydajnościowych.

IIS (Windows)

1) Otwórz web.config w katalogu aplikacji.

2) Wstaw do sekcji system.webServer/httpProtocol/customHeaders elementy add, np.:

  • <add name=”Strict-Transport-Security” value=”max-age=31536000″ />
  • <add name=”X-Frame-Options” value=”DENY” />
  • <add name=”X-Content-Type-Options” value=”nosniff” />
  • <add name=”Referrer-Policy” value=”strict-origin-when-cross-origin” />
  • <add name=”Permissions-Policy” value=”geolocation=(), microphone=(), camera=()” />
  • <add name=”Cross-Origin-Opener-Policy” value=”same-origin” />
  • <add name=”Cross-Origin-Embedder-Policy” value=”require-corp” />
  • <add name=”Cross-Origin-Resource-Policy” value=”same-origin” />
  • <add name=”Content-Security-Policy” value=”default-src 'self’; object-src 'none’; base-uri 'self’; frame-ancestors 'none'” />

3) Zrestartuj aplikację w IIS Manager lub poleceniem iisreset. CSP z nonce realizuj w warstwie aplikacji (np. ASP.NET), generując nonce per żądanie i wstawiając do skryptów.

Node.js/Express i inne frameworki

1) Express: zainstaluj helmet (zależnie od wersji frameworka i Node). W kodzie aplikacji użyj helmet i skonfiguruj sekcje dla CSP, HSTS, frameguard (X-Frame-Options), noSniff (X-Content-Type-Options), referrerPolicy itd.

2) PHP: jeśli korzystasz z Apache/Nginx, preferuj ustawienia na poziomie serwera. Dodatkowo możesz wysyłać nagłówki funkcją header(…) z warstwy PHP. W przypadku CSP nonce generuj w PHP (np. losowe 16–24 bajty, Base64), wstaw do nagłówka i znaczników script.

3) Django/Flask: użyj middleware (django-secure, django-csp) lub własnych middlewarów. Analogicznie generuj nonce i egzekwuj polityki.

4) Serwery SSR/SPA: jeśli korzystasz z Next.js, Nuxt, Vite SSR – skonfiguruj nagłówki na warstwie proxy (Nginx) lub za pomocą mechanizmów serwera platformy (np. next.config.js dla nagłówków statycznych zasobów, a dla HTML – middleware).

Budowa bezpiecznej polityki CSP krok po kroku

Inwentaryzacja zasobów i minimalny szkielet

Zacznij od spisu źródeł zasobów: domeny skryptów, stylów, obrazów, fontów, mediów, połączeń XHR/WebSocket, ramek. Minimalny szkielet polityki:

  • default-src 'self’
  • object-src 'none’
  • base-uri 'self’
  • frame-ancestors 'none’ (lub 'self’ jeśli osadzasz panel w innych częściach witryny)
  • form-action 'self’ (jeśli wysyłasz formularze)
  • img-src 'self’ data: (jeśli naprawdę potrzebujesz data: dla obrazów; inaczej ogranicz)

Skrypty i style:

  • script-src 'self’ 'nonce-RANDOM’ lub z hashami 'sha256-…’; unikaj 'unsafe-inline’ i 'unsafe-eval’.
  • style-src 'self’ 'nonce-RANDOM’ lub, jeśli to niemożliwe, minimalizuj 'unsafe-inline’ i przejdź na nonce/hash w kolejnych iteracjach.

Połączenia sieciowe:

  • connect-src 'self’ api.twojadomena.tld w zależności od potrzeb (SSE/WS/XHR/fetch).
  • font-src 'self’ cdn.twojadomena.tld (jeżeli hostujesz fonty na CDN).
  • media-src, frame-src tylko w razie konieczności.

Tryb Report-Only i zbieranie raportów

Przed egzekwowaniem użyj Content-Security-Policy-Report-Only, aby zaobserwować naruszenia bez blokowania:

  • Content-Security-Policy-Report-Only: default-src 'self’; …; report-to default-endpoint
  • Reporting-Endpoints lub Report-To: skonfiguruj endpoint raportowania, np. https://report.twojadomena.tld/csp-report.

Po kilku dniach/tygodniach analizy logów poprawiaj politykę i sukcesywnie przełączaj na Content-Security-Policy (tryb egzekwowania). Uwaga: niektóre przeglądarki mogą różnie obsługiwać Report-To i Reporting-Endpoints – przetestuj kilka.

Nonce i hash – wybór strategii

  • Nonce: generowany per-odpowiedź, wstawiany w nagłówku CSP (script-src 'nonce-…’) i w atrybucie nonce każdego dozwolonego znacznika script/style. Wymaga ingerencji w HTML generowany przez serwer/aplikację.
  • Hash: obliczony z treści skryptu inline (sha256/384/512). Stabilny, jeżeli skrypt nie zmienia się dynamicznie. Dla wielu małych inline’ów może być trudniejszy w utrzymaniu.
  • Unikaj 'unsafe-inline’ i 'unsafe-eval’. Jeżeli biblioteka wymaga eval, poszukaj alternatywy lub ogranicz użycie do wąskich kontekstów.

Typowe integracje i wyjątki

  • CDN ze skryptami: dodaj do script-src konkretną domenę CDN; rozważ Subresource Integrity (SRI) i stopniową migrację do hostowania własnego.
  • Mapy, płatności, wideo: często wymagają frame-src i connect-src – dokumentuj każdy wyjątek i utrzymuj minimalny zakres.
  • Analytics: preferuj self-hosted lub minimalny, ściśle wskazany zestaw domen. Wyłącz wildcardy typu *.example.com, jeśli nie musisz ich mieć.
  • Service Worker: pamiętaj o script-src dla pliku SW i o tym, że SW może buforować odpowiedzi – zgodność nagłówków po zmianach CSP jest kluczowa.

Testowanie, wdrażanie etapowe i monitorowanie

Narzędzia testowe

  • curl -I https://twojadomena.tld – szybka kontrola obecności nagłówków.
  • Developer Tools w przeglądarce (zakładka Network, Security, Console) – zobaczysz naruszenia CSP, COOP/COEP/CORP.
  • securityheaders.com – ocena zestawu nagłówków i wskazówki.
  • Mozilla Observatory – skan i rekomendacje.
  • Qualys SSL Labs – weryfikacja HTTPS, pomocna przy HSTS.

Wdrażanie etapowe

  • Faza 0: Uruchom nagłówki niskiego ryzyka (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, wstępny HSTS bez includeSubDomains i bez preload).
  • Faza 1: Dodaj Permissions-Policy, CORP, COOP, COEP z ostrożnymi wartościami. Monitoruj konsolę przeglądarki i logi.
  • Faza 2: CSP w Report-Only. Zbieraj raporty (Reporting-Endpoints, logi serwera), usuwaj niepotrzebne inline’y, dodawaj nonce/hash.
  • Faza 3: Przełącz CSP na egzekwowanie. Utrzymuj raportowanie, aby szybko wykrywać regresje.
  • Faza 4: Po 4–8 tygodniach stabilności rozważ HSTS includeSubDomains i preload.

Raportowanie i observability

  • Reporting-Endpoints: skonfiguruj niezależny, wysoko dostępny endpoint do zbierania raportów CSP/COOP/COEP. Przechowuj próbki (np. 30 dni), buduj dashboard (np. w ELK, Grafana, Datadog).
  • Alerting: reguły na gwałtowne wzrosty naruszeń per ścieżka, per przeglądarka, per release.
  • Korelacja z wersją: dodawaj nagłówek X-Release lub parametry wersji, aby łączyć naruszenia z konkretnym wdrożeniem.

CI/CD i rollback

  • Testy E2E i smoke: skrypty, które weryfikują kluczowe flow pod kątem nagłówków (np. sprawdzają obecność i wartości przez curl lub biblioteki HTTP).
  • Feature flags: przełączniki, które pozwalają szybko zmiękczyć politykę (np. dodać tymczasowo dodatkowe domeny w CSP) w razie incydentu.
  • Canary i procentowe rollouty: najpierw mały procent ruchu, potem 100% po weryfikacji raportów.

Scenariusze zaawansowane i częste pułapki

CDN, reverse proxy i Kubernetes

  • Cloudflare: ustaw HTTP Response Headers w Rules lub Transform Rules; pamiętaj o bypass dla zasobów, które muszą mieć inne polityki (np. pliki do osadzenia).
  • Fastly/Akamai: dodaj custom response headers w VCL/konfiguracji; testuj różnice między edge a origin.
  • Kubernetes: w NGINX Ingress użyj annotations do dodawania nagłówków (nginx.ingress.kubernetes.io/configuration-snippet lub add-header w ConfigMap). Upewnij się, że nie dublujesz nagłówków w samym podzie.
  • Load balancery (ALB/ELB): jeśli wstrzykujesz nagłówki na LB, nie powielaj ich w aplikacji – trzymaj jedno źródło prawdy.

CORS vs CORP/COEP/COOP

  • CORS reguluje, czy przeglądarka może odczytać zasób z innego originu; CORP/COEP/COOP dotyczą izolacji i osadzania. Niezależne od siebie – konfiguruj świadomie.
  • COEP: require-corp wymaga, aby osadzane zasoby zwracały CORP: same-origin lub cross-origin z odpowiednimi nagłówkami.
  • COOP: same-origin izoluje twoje okno od cross-origin. Zwróć uwagę na integracje typu płatności i otwieranie nowych okien – przetestuj.

Cache, nagłówki a zasoby statyczne

  • CDN może buforować odpowiedzi z nagłówkami. Jeśli zmienisz politykę, zasoby w cache mogą mieć stare wartości. Stosuj wersjonowanie ścieżek, kontroluj Cache-Control dla HTML vs. assetów.
  • HTML powinien być krótko cache’owany lub no-store, a polityka CSP/HSTS w nim – aktualna. Assety mogą mieć długi cache, ale zwykle nie niosą CSP; uważaj na CORP/COEP przy osadzaniu.
  • Redirekty: dodaj nagłówki również na 301/302 (w Nginx/Apache używaj always), inaczej pierwsze żądanie nie otrzyma np. HSTS.

Migracje i zgodność przeglądarek

  • Starsze przeglądarki mogą ignorować część dyrektyw (np. zaawansowane CSP). Projektuj politykę tak, by degradacja była bezpieczna.
  • X-XSS-Protection: przestarzały, bywa ignorowany; polegaj na CSP i bezpiecznym kodowaniu danych.
  • HSTS preload: usunięcie z listy trwa długo; zanim dodasz preload, upewnij się, że cała domena i subdomeny są dostępne wyłącznie przez HTTPS.
  • Mixed content: włącz upgrade-insecure-requests w CSP, ale finalnie usuń wszystkie linki HTTP – to tylko koło ratunkowe.

Checklist przed produkcją

  • HTTPS wszędzie, certyfikaty ważne i automatycznie odnawiane.
  • HSTS bez preloadu działa poprawnie przez kilka tygodni; brak raportów o problemach subdomen.
  • CSP w Report-Only nie generuje krytycznych naruszeń; po przełączeniu na egzekwowanie aplikacja działa.
  • Permissions-Policy ogranicza nieużywane API; brak regresji funkcji.
  • COOP/COEP/CORP nie blokują potrzebnych osadzeń; integracje zewnętrzne (płatności, wideo, SSO) przetestowane.
  • Automaty testowe sprawdzają nagłówki na kluczowych ścieżkach (publiczne, logowanie, panel, API).

Gotowe wzorce wartości i przykładowe zestawy

Zestaw konserwatywny (start)

  • Strict-Transport-Security: max-age=31536000
  • X-Frame-Options: DENY
  • X-Content-Type-Options: nosniff
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy: geolocation=(), microphone=(), camera=()
  • Cross-Origin-Opener-Policy: same-origin
  • Cross-Origin-Embedder-Policy: require-corp
  • Cross-Origin-Resource-Policy: same-origin
  • Content-Security-Policy: default-src 'self’; object-src 'none’; base-uri 'self’; frame-ancestors 'none’; img-src 'self’; script-src 'self’; style-src 'self’

Uwaga: jeżeli korzystasz z fontów/obrazów z CDN, dodaj odpowiednie domeny. W przypadku inline stylów/skryptów – przejdź na nonce/hash.

Zestaw dla aplikacji SPA z CDN

  • Strict-Transport-Security: max-age=31536000; includeSubDomains
  • X-Frame-Options: SAMEORIGIN (jeśli panel osadzasz wewnętrznie) lub DENY
  • X-Content-Type-Options: nosniff
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy: geolocation=(), microphone=(), camera=(), usb=()
  • Cross-Origin-Opener-Policy: same-origin
  • Cross-Origin-Embedder-Policy: require-corp
  • Cross-Origin-Resource-Policy: same-origin (dla krytycznych), cross-origin (dla publicznych assetów, jeśli to konieczne)
  • Content-Security-Policy: default-src 'self’; object-src 'none’; base-uri 'self’; frame-ancestors 'none’; connect-src 'self’ api.twojadomena.tld; img-src 'self’ data: cdn.twojadomena.tld; font-src 'self’ cdn.twojadomena.tld; script-src 'self’ cdn.twojadomena.tld 'nonce-RANDOM’; style-src 'self’ cdn.twojadomena.tld 'nonce-RANDOM’; upgrade-insecure-requests

Zestaw dla serwisów z osadzaniem ramek

  • Zamiast X-Frame-Options: używaj CSP frame-ancestors, które precyzyjniej kontroluje osadzanie (np. frame-ancestors https://partner.example).
  • Dodaj frame-src dozwolone źródła, jeśli wyświetlasz cudze treści w iframe.
  • Pilnuj, by domeny partnerów były ściśle wylistowane – unikaj wildcardów.

Zmiany bez przestojów

  • Wersjonuj politykę (komentarz, osobny plik konfiguracyjny) i używaj rolloutów procentowych na warstwie LB/CDN.
  • Utrzymuj listę wyjątków z datą dodania i celem usunięcia. Przeglądaj co sprint.
  • Dokumentuj powiązania między polityką a komponentami front-end (np. które moduły wymagają dodatkowych dyrektyw).
< Powrót

Zapisz się do newslettera


Zadzwoń Napisz