HTTP Security Headers — 7 obaveznih headera

Kompletni vodic za konfiguraciju bezbednosnih headera koji stite vas sajt od XSS, clickjacking i drugih napada

93%
Sajtova nema CSP header
7
Kljucnih security headera
A+
Mozilla Observatory cilj

1. Sta su HTTP security headeri

HTTP headeri su meta-podaci koji se razmenjuju izmedju klijenta (browser-a) i servera prilikom svakog HTTP zahteva i odgovora. Security headeri su specificna podgrupa response headera koji instruisu browser da primeni bezbednosne politike.

Bez security headera, vas sajt je podlozan:

  • XSS napadima — Napadac ubacuje maliciozne skripte (sprecava CSP)
  • Clickjacking napadima — Vas sajt se ucitava u nevidljiv iframe (sprecava X-Frame-Options)
  • MIME sniffing napadima — Browser pogresno interpretira tip fajla (sprecava X-Content-Type-Options)
  • Downgrade napadima — Forsiranje HTTP umesto HTTPS (sprecava HSTS)
  • Data leaking — Curenje podataka kroz Referer header (sprecava Referrer-Policy)

Prema Mozilla Observatory podacima, samo oko 7% sajtova ima implementiran Content-Security-Policy header, sto znaci da je ogromna vecina web sajtova ranjiva na napade koje CSP moze da spreci.

Referenca: OWASP Secure Headers Project

2. Strict-Transport-Security (HSTS)

HSTS je header koji nareduje browser-u da uvek koristi HTTPS za komunikaciju sa vasim sajtom. Definisan je u RFC 6797.

Zasto je HSTS vazan

Bez HSTS-a, cak i sajtovi sa HTTPS-om su ranjivi na SSL stripping napad (alat: sslstrip). Napadac na istoj mrezi moze presresti prvu HTTP konekciju pre nego sto se redirekcija na HTTPS izvrsi.

Parametri

  • max-age — Vreme u sekundama koliko browser pamti da koristi HTTPS (preporuka: 63072000 = 2 godine)
  • includeSubDomains — Primenjuje politiku i na sve poddomene
  • preload — Dozvoljava dodavanje na browser-ovu preload listu

Konfiguracija

# Nginx
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

# Apache
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"

# Express.js (sa helmet middleware-om)
const helmet = require('helmet');
app.use(helmet.hsts({
  maxAge: 63072000,
  includeSubDomains: true,
  preload: true
}));

Referenca: MDN — Strict-Transport-Security

3. Content-Security-Policy (CSP)

Content-Security-Policy je najmocaniji security header. On kontrolise koje resurse browser sme da ucita i izvrsi na vasoj stranici. CSP je definisan u W3C CSP Level 3 specifikaciji.

Kljucne direktive

DirektivaKontrolisePrimer
default-srcFallback za sve tipove'self'
script-srcJavaScript izvore'self' 'nonce-abc123'
style-srcCSS izvore'self' 'unsafe-inline'
img-srcIzvore slika'self' data: https:
connect-srcAPI/fetch/WebSocket'self' https://api.example.com
font-srcFont izvore'self' https://fonts.gstatic.com
frame-srcIframe izvore'none'
object-srcPlugin-e (Flash, Java)'none'
base-uri<base> tag'self'
form-actionForme (action URL)'self'
report-uriURL za prijavu krsenja/csp-report

Primer stroge CSP politike

# Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-RANDOM'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;

# Apache
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; frame-ancestors 'none';"

# Express.js
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'"],
    styleSrc: ["'self'", "'unsafe-inline'"],
    imgSrc: ["'self'", "data:", "https:"],
    objectSrc: ["'none'"],
    frameAncestors: ["'none'"]
  }
}));
Report-Only mod: Pre primene stroge CSP politike, koristite Content-Security-Policy-Report-Only header. On prijavljuje krsenja bez blokiranja resursa, sto vam omogucava da testirate politiku bez rizika da pokvarite sajt.
Izbegavajte 'unsafe-inline' i 'unsafe-eval': Ove vrednosti znacajno oslabljuju CSP. Umesto 'unsafe-inline' za skripte, koristite nonce-based pristup ('nonce-abc123') ili hash-based pristup ('sha256-...').

Referenca: MDN — Content Security Policy

4. X-Frame-Options

X-Frame-Options kontrolise da li se vasa stranica moze prikazati unutar <iframe>, <frame> ili <object> elementa. Ovo je kljucna zastita od clickjacking napada.

Sta je clickjacking

U clickjacking napadu, napadac ucitava vas sajt u nevidljiv iframe na svojoj stranici. Korisnik misli da klikce na napadacevu stranicu, ali zapravo klikce na dugmad na vasem sajtu — moze nesvesno promeniti lozinku, obrisati nalog ili izvrsiti transakciju.

Vrednosti

  • DENY — Stranica se nikada ne moze prikazati u iframe-u (najbezbednije)
  • SAMEORIGIN — Samo sa istog domena
  • ALLOW-FROM uri — Dozvoljeno samo sa navedenog URI-ja (deprecated, slaba podrska)

Konfiguracija

# Nginx
add_header X-Frame-Options "DENY" always;

# Apache
Header always set X-Frame-Options "DENY"

# Express.js
app.use(helmet.frameguard({ action: 'deny' }));
Napomena: CSP direktiva frame-ancestors je modernija zamena za X-Frame-Options i pruza vecu fleksibilnost. Preporuka je da koristite oba headera za maksimalnu kompatibilnost.

Referenca: MDN — X-Frame-Options

5. X-Content-Type-Options

Ovaj header sprecava MIME type sniffing — ponasanje browser-a gde on ignorise deklarisani Content-Type i pokusava da "pogodi" tip sadrzaja na osnovu samog sadrzaja fajla.

Kako MIME sniffing napad radi

Napadac uploada fajl koji izgleda kao slika (npr. image.jpg) ali zapravo sadrzi JavaScript kod. Bez ovog headera, browser moze da izvrsi taj kod umesto da ga prikaze kao sliku. Ovo je posebno opasno na sajtovima koji dozvoljavaju upload fajlova.

Jedina vrednost

X-Content-Type-Options: nosniff

Konfiguracija

# Nginx
add_header X-Content-Type-Options "nosniff" always;

# Apache
Header always set X-Content-Type-Options "nosniff"

# Express.js
app.use(helmet.noSniff());

Referenca: MDN — X-Content-Type-Options

6. Referrer-Policy

Referrer-Policy kontrolise koliko informacija o URL-u se salje u Referer header-u kada korisnik navigira sa vaseg sajta na drugi sajt. Ovo je vazno za privatnost korisnika i sprecavanje curenja osetljivih podataka u URL-ovima.

Dostupne opcije

VrednostPonasanjePreporuka
no-referrerNikada ne salje RefererMaksimalna privatnost
no-referrer-when-downgradeSalje za HTTPS->HTTPS, ne za HTTPS->HTTPBrowser default
originSalje samo origin (domen bez putanje)Dobar balans
origin-when-cross-originPun URL za isti domen, samo origin za drugiDobar balans
same-originSalje samo za isti domenStroga privatnost
strict-originSamo origin, ne za downgradePreporuceno
strict-origin-when-cross-originPun URL za isti domen, origin za drugi, nista za downgradeNajbolja preporuka
unsafe-urlUvek salje pun URLNe koristiti

Konfiguracija

# Nginx
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# Apache
Header always set Referrer-Policy "strict-origin-when-cross-origin"

# Express.js
app.use(helmet.referrerPolicy({
  policy: 'strict-origin-when-cross-origin'
}));

Referenca: MDN — Referrer-Policy

7. Permissions-Policy

Permissions-Policy (ranije Feature-Policy) kontrolise koje browser API-je i funkcije vas sajt i ugradjeni iframe-ovi smeju da koriste. Ovo sprecava zloupotrebe poput neovlascenog pristupa kameri, mikrofonu ili lokaciji.

Kljucne funkcije za kontrolu

  • camera — Pristup kameri
  • microphone — Pristup mikrofonu
  • geolocation — Pristup lokaciji
  • payment — Payment Request API
  • usb — WebUSB API
  • autoplay — Automatsko pustanje medija
  • fullscreen — Fullscreen API
  • interest-cohort — FLoC (Google Privacy Sandbox)

Konfiguracija

# Nginx
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()" always;

# Apache
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()"

# Express.js
app.use(helmet.permittedCrossDomainPolicies());
// Ili rucno:
app.use((req, res, next) => {
  res.setHeader('Permissions-Policy',
    'camera=(), microphone=(), geolocation=(), payment=()');
  next();
});
Sintaksa: () znaci "zabranjeno za sve", (self) znaci "dozvoljeno samo za vas domen", (self "https://primer.com") znaci "dozvoljeno za vas domen i navedeni sajt".

Referenca: MDN — Permissions-Policy

8. Cross-Origin-Opener-Policy (COOP)

COOP izoluje vas browsing kontekst od cross-origin prozora. Ovo je zastita od Spectre-klase napada koji mogu da procitaju memoriju iz drugih procesa browser-a.

Zasto je COOP vazan

Spectre napadi (CVE-2017-5753, CVE-2017-5715) su otkrili da JavaScript u browser-u moze da cita memoriju iz drugih procesa koristeci speculative execution. COOP, zajedno sa COEP (Cross-Origin-Embedder-Policy), omogucava site isolation i pristup high-resolution timer-ima (SharedArrayBuffer) na bezbedan nacin.

Vrednosti

  • unsafe-none — Nema izolacije (default)
  • same-origin — Izoluje prozor od cross-origin opener-a
  • same-origin-allow-popups — Izolacija, ali dozvoljava popupe

Konfiguracija

# Nginx
add_header Cross-Origin-Opener-Policy "same-origin" always;

# Apache
Header always set Cross-Origin-Opener-Policy "same-origin"

# Express.js
app.use(helmet.crossOriginOpenerPolicy({ policy: 'same-origin' }));

Referenca: MDN — Cross-Origin-Opener-Policy

9. Pregled svih headera

HeaderPreporucena vrednostStiti odSeverity
Strict-Transport-Securitymax-age=63072000; includeSubDomains; preloadSSL stripping, downgradeKriticno
Content-Security-Policydefault-src 'self'; script-src 'self'XSS, injectionKriticno
X-Frame-OptionsDENYClickjackingVisoko
X-Content-Type-OptionsnosniffMIME sniffingSrednje
Referrer-Policystrict-origin-when-cross-originData leakingSrednje
Permissions-Policycamera=(), microphone=()Neovlasceni pristupSrednje
Cross-Origin-Opener-Policysame-originSpectre napadiSrednje

Kompletna Nginx konfiguracija

# Dodajte u server blok ili http blok
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" 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 "camera=(), microphone=(), geolocation=(), payment=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;

10. Reference i resursi

Skenirajte security headere vaseg sajta

HTTP Security Headers — 7 essential headers

Complete guide to configuring security headers that protect your site from XSS, clickjacking, and other attacks

93%
Sites lack CSP header
7
Key security headers
A+
Mozilla Observatory goal

1. What are HTTP security headers

HTTP headers are metadata exchanged between the client (browser) and server with every HTTP request and response. Security headers are a specific subset of response headers that instruct the browser to enforce security policies.

Without security headers, your site is vulnerable to:

  • XSS attacks — Attacker injects malicious scripts (prevented by CSP)
  • Clickjacking attacks — Your site is loaded in an invisible iframe (prevented by X-Frame-Options)
  • MIME sniffing attacks — Browser misinterprets file type (prevented by X-Content-Type-Options)
  • Downgrade attacks — Forcing HTTP instead of HTTPS (prevented by HSTS)
  • Data leaking — Data leakage through the Referer header (prevented by Referrer-Policy)

According to Mozilla Observatory data, only about 7% of websites have the Content-Security-Policy header implemented, meaning the vast majority of websites are vulnerable to attacks that CSP can prevent.

Reference: OWASP Secure Headers Project

2. Strict-Transport-Security (HSTS)

HSTS is a header that instructs the browser to always use HTTPS when communicating with your site. It is defined in RFC 6797.

Why HSTS matters

Without HSTS, even sites with HTTPS are vulnerable to SSL stripping attacks (tool: sslstrip). An attacker on the same network can intercept the initial HTTP connection before the HTTPS redirect occurs.

Parameters

  • max-age — Time in seconds the browser remembers to use HTTPS (recommended: 63072000 = 2 years)
  • includeSubDomains — Applies the policy to all subdomains
  • preload — Allows addition to the browser's preload list

Configuration

# Nginx
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

# Apache
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"

# Express.js (with helmet middleware)
const helmet = require('helmet');
app.use(helmet.hsts({
  maxAge: 63072000,
  includeSubDomains: true,
  preload: true
}));

Reference: MDN — Strict-Transport-Security

3. Content-Security-Policy (CSP)

Content-Security-Policy is the most powerful security header. It controls which resources the browser is allowed to load and execute on your page. CSP is defined in the W3C CSP Level 3 specification.

Key directives

DirectiveControlsExample
default-srcFallback for all types'self'
script-srcJavaScript sources'self' 'nonce-abc123'
style-srcCSS sources'self' 'unsafe-inline'
img-srcImage sources'self' data: https:
connect-srcAPI/fetch/WebSocket'self' https://api.example.com
font-srcFont sources'self' https://fonts.gstatic.com
frame-srcIframe sources'none'
object-srcPlugins (Flash, Java)'none'
report-uriURL for violation reports/csp-report

Strict CSP policy example

# Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-RANDOM'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;

# Apache
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; frame-ancestors 'none';"

# Express.js
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'"],
    styleSrc: ["'self'", "'unsafe-inline'"],
    imgSrc: ["'self'", "data:", "https:"],
    objectSrc: ["'none'"],
    frameAncestors: ["'none'"]
  }
}));
Report-Only mode: Before enforcing a strict CSP policy, use the Content-Security-Policy-Report-Only header. It reports violations without blocking resources, allowing you to test the policy without risking breaking your site.
Avoid 'unsafe-inline' and 'unsafe-eval': These values significantly weaken CSP. Instead of 'unsafe-inline' for scripts, use nonce-based approach ('nonce-abc123') or hash-based approach ('sha256-...').

Reference: MDN — Content Security Policy

4. X-Frame-Options

X-Frame-Options controls whether your page can be displayed inside an <iframe>, <frame>, or <object> element. This is a key protection against clickjacking attacks.

What is clickjacking

In a clickjacking attack, the attacker loads your site in an invisible iframe on their page. The user thinks they are clicking on the attacker's page, but they are actually clicking buttons on your site — they can unknowingly change their password, delete their account, or execute a transaction.

Values

  • DENY — Page can never be displayed in an iframe (most secure)
  • SAMEORIGIN — Only from the same origin
  • ALLOW-FROM uri — Allowed only from the specified URI (deprecated, poor support)

Configuration

# Nginx
add_header X-Frame-Options "DENY" always;

# Apache
Header always set X-Frame-Options "DENY"

# Express.js
app.use(helmet.frameguard({ action: 'deny' }));

Reference: MDN — X-Frame-Options

5. X-Content-Type-Options

This header prevents MIME type sniffing — a browser behavior where it ignores the declared Content-Type and tries to "guess" the content type based on the file contents itself.

How MIME sniffing attacks work

An attacker uploads a file that looks like an image (e.g., image.jpg) but actually contains JavaScript code. Without this header, the browser may execute that code instead of displaying it as an image. This is especially dangerous on sites that allow file uploads.

Configuration

# Nginx
add_header X-Content-Type-Options "nosniff" always;

# Apache
Header always set X-Content-Type-Options "nosniff"

# Express.js
app.use(helmet.noSniff());

Reference: MDN — X-Content-Type-Options

6. Referrer-Policy

Referrer-Policy controls how much URL information is sent in the Referer header when a user navigates from your site to another site. This is important for user privacy and preventing sensitive data leakage in URLs.

Available options

ValueBehaviorRecommendation
no-referrerNever sends RefererMaximum privacy
originSends only origin (domain without path)Good balance
strict-originOnly origin, not on downgradeRecommended
strict-origin-when-cross-originFull URL same-origin, origin cross-origin, nothing on downgradeBest recommendation
unsafe-urlAlways sends full URLDo not use

Configuration

# Nginx
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# Apache
Header always set Referrer-Policy "strict-origin-when-cross-origin"

# Express.js
app.use(helmet.referrerPolicy({
  policy: 'strict-origin-when-cross-origin'
}));

Reference: MDN — Referrer-Policy

7. Permissions-Policy

Permissions-Policy (formerly Feature-Policy) controls which browser APIs and features your site and embedded iframes are allowed to use. This prevents abuse such as unauthorized access to camera, microphone, or location.

Key features to control

  • camera — Camera access
  • microphone — Microphone access
  • geolocation — Location access
  • payment — Payment Request API
  • usb — WebUSB API
  • autoplay — Auto-playing media
  • fullscreen — Fullscreen API

Configuration

# Nginx
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()" always;

# Apache
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()"

# Express.js
app.use((req, res, next) => {
  res.setHeader('Permissions-Policy',
    'camera=(), microphone=(), geolocation=(), payment=()');
  next();
});

Reference: MDN — Permissions-Policy

8. Cross-Origin-Opener-Policy (COOP)

COOP isolates your browsing context from cross-origin windows. This protects against Spectre-class attacks that can read memory from other browser processes.

Why COOP matters

Spectre attacks (CVE-2017-5753, CVE-2017-5715) revealed that JavaScript in the browser can read memory from other processes using speculative execution. COOP, together with COEP (Cross-Origin-Embedder-Policy), enables site isolation and safe access to high-resolution timers (SharedArrayBuffer).

Values

  • unsafe-none — No isolation (default)
  • same-origin — Isolates window from cross-origin openers
  • same-origin-allow-popups — Isolation, but allows popups

Configuration

# Nginx
add_header Cross-Origin-Opener-Policy "same-origin" always;

# Apache
Header always set Cross-Origin-Opener-Policy "same-origin"

# Express.js
app.use(helmet.crossOriginOpenerPolicy({ policy: 'same-origin' }));

Reference: MDN — Cross-Origin-Opener-Policy

9. Complete header overview

HeaderRecommended valueProtects againstSeverity
Strict-Transport-Securitymax-age=63072000; includeSubDomains; preloadSSL stripping, downgradeCritical
Content-Security-Policydefault-src 'self'; script-src 'self'XSS, injectionCritical
X-Frame-OptionsDENYClickjackingHigh
X-Content-Type-OptionsnosniffMIME sniffingMedium
Referrer-Policystrict-origin-when-cross-originData leakingMedium
Permissions-Policycamera=(), microphone=()Unauthorized accessMedium
Cross-Origin-Opener-Policysame-originSpectre attacksMedium

Complete Nginx configuration

# Add to server or http block
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" 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 "camera=(), microphone=(), geolocation=(), payment=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;

10. References and resources

Scan your site's security headers