HTTP Security Headers — 7 obaveznih headera
Kompletni vodic za konfiguraciju bezbednosnih headera koji stite vas sajt od XSS, clickjacking i drugih napada
Sadrzaj
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 poddomenepreload— 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
| Direktiva | Kontrolise | Primer |
|---|---|---|
default-src | Fallback za sve tipove | 'self' |
script-src | JavaScript izvore | 'self' 'nonce-abc123' |
style-src | CSS izvore | 'self' 'unsafe-inline' |
img-src | Izvore slika | 'self' data: https: |
connect-src | API/fetch/WebSocket | 'self' https://api.example.com |
font-src | Font izvore | 'self' https://fonts.gstatic.com |
frame-src | Iframe izvore | 'none' |
object-src | Plugin-e (Flash, Java) | 'none' |
base-uri | <base> tag | 'self' |
form-action | Forme (action URL) | 'self' |
report-uri | URL 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'"]
}
}));
Content-Security-Policy-Report-Only header. On prijavljuje krsenja bez blokiranja resursa, sto vam omogucava da testirate politiku bez rizika da pokvarite sajt.
'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 domenaALLOW-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' }));
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
| Vrednost | Ponasanje | Preporuka |
|---|---|---|
no-referrer | Nikada ne salje Referer | Maksimalna privatnost |
no-referrer-when-downgrade | Salje za HTTPS->HTTPS, ne za HTTPS->HTTP | Browser default |
origin | Salje samo origin (domen bez putanje) | Dobar balans |
origin-when-cross-origin | Pun URL za isti domen, samo origin za drugi | Dobar balans |
same-origin | Salje samo za isti domen | Stroga privatnost |
strict-origin | Samo origin, ne za downgrade | Preporuceno |
strict-origin-when-cross-origin | Pun URL za isti domen, origin za drugi, nista za downgrade | Najbolja preporuka |
unsafe-url | Uvek salje pun URL | Ne 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 kamerimicrophone— Pristup mikrofonugeolocation— Pristup lokacijipayment— Payment Request APIusb— WebUSB APIautoplay— Automatsko pustanje medijafullscreen— Fullscreen APIinterest-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();
});
() 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-asame-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
| Header | Preporucena vrednost | Stiti od | Severity |
|---|---|---|---|
| Strict-Transport-Security | max-age=63072000; includeSubDomains; preload | SSL stripping, downgrade | Kriticno |
| Content-Security-Policy | default-src 'self'; script-src 'self' | XSS, injection | Kriticno |
| X-Frame-Options | DENY | Clickjacking | Visoko |
| X-Content-Type-Options | nosniff | MIME sniffing | Srednje |
| Referrer-Policy | strict-origin-when-cross-origin | Data leaking | Srednje |
| Permissions-Policy | camera=(), microphone=() | Neovlasceni pristup | Srednje |
| Cross-Origin-Opener-Policy | same-origin | Spectre napadi | Srednje |
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
- OWASP Secure Headers Project — Kompletna lista preporucenih headera
- Mozilla Observatory — Besplatan skener za security headere
- SecurityHeaders.com — Brzi skener headera
- MDN — HTTP Headers — Kompletna referenca
- Helmet.js — Security headers middleware za Express.js
- RFC 6797 — HSTS
- W3C — Content Security Policy Level 3
- OWASP HTTP Headers Cheat Sheet
HTTP Security Headers — 7 essential headers
Complete guide to configuring security headers that protect your site from XSS, clickjacking, and other attacks
Table of Contents
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 subdomainspreload— 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
| Directive | Controls | Example |
|---|---|---|
default-src | Fallback for all types | 'self' |
script-src | JavaScript sources | 'self' 'nonce-abc123' |
style-src | CSS sources | 'self' 'unsafe-inline' |
img-src | Image sources | 'self' data: https: |
connect-src | API/fetch/WebSocket | 'self' https://api.example.com |
font-src | Font sources | 'self' https://fonts.gstatic.com |
frame-src | Iframe sources | 'none' |
object-src | Plugins (Flash, Java) | 'none' |
report-uri | URL 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'"]
}
}));
Content-Security-Policy-Report-Only header. It reports violations without blocking resources, allowing you to test the policy without risking breaking your site.
'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 originALLOW-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
| Value | Behavior | Recommendation |
|---|---|---|
no-referrer | Never sends Referer | Maximum privacy |
origin | Sends only origin (domain without path) | Good balance |
strict-origin | Only origin, not on downgrade | Recommended |
strict-origin-when-cross-origin | Full URL same-origin, origin cross-origin, nothing on downgrade | Best recommendation |
unsafe-url | Always sends full URL | Do 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 accessmicrophone— Microphone accessgeolocation— Location accesspayment— Payment Request APIusb— WebUSB APIautoplay— Auto-playing mediafullscreen— 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 openerssame-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
| Header | Recommended value | Protects against | Severity |
|---|---|---|---|
| Strict-Transport-Security | max-age=63072000; includeSubDomains; preload | SSL stripping, downgrade | Critical |
| Content-Security-Policy | default-src 'self'; script-src 'self' | XSS, injection | Critical |
| X-Frame-Options | DENY | Clickjacking | High |
| X-Content-Type-Options | nosniff | MIME sniffing | Medium |
| Referrer-Policy | strict-origin-when-cross-origin | Data leaking | Medium |
| Permissions-Policy | camera=(), microphone=() | Unauthorized access | Medium |
| Cross-Origin-Opener-Policy | same-origin | Spectre attacks | Medium |
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
- OWASP Secure Headers Project — Complete list of recommended headers
- Mozilla Observatory — Free security header scanner
- SecurityHeaders.com — Quick header scanner
- MDN — HTTP Headers — Complete reference
- Helmet.js — Security headers middleware for Express.js
- RFC 6797 — HSTS
- W3C — Content Security Policy Level 3
- OWASP HTTP Headers Cheat Sheet