HTTP security headers are free, server-side response headers that instruct browsers to block entire classes of attack — cross-site scripting, clickjacking, SSL stripping, MIME sniffing — before a single line of attacker code can execute. For Indian businesses operating web applications, configuring these headers correctly is one of the highest-impact, lowest-cost hardening steps available. A misconfigured or absent header is a finding on every VAPT report, graded from Medium to Critical depending on the header and the attack it leaves open. This article covers each key header, what it defends against, recommended values, and how to roll out the hardest one (CSP) without breaking your site.
Why HTTP Security Headers Matter
When your server returns a response, it can include instructions telling the browser how to handle that response. These instructions live in HTTP response headers. They cost nothing to add — no external service, no library, no certificate purchase. Yet the OWASP Secure Headers Project lists their absence as one of the most consistently observed weaknesses across web applications globally.
Without them, the browser operates in a permissive default mode: it will execute any inline JavaScript, load resources from any origin, allow your page to be framed by any domain, and happily downgrade from HTTPS to HTTP if an attacker intercepts the connection. Each of those defaults is a threat vector.
The Full Header Map: Threat, Header, Value
The table below maps each header to the threat it addresses and the recommended configuration for production.
| Header | Threat Mitigated | Recommended Value |
|---|---|---|
Content-Security-Policy | XSS, injection, data exfiltration | Per-app policy; start with report-only |
Strict-Transport-Security | SSL stripping, protocol downgrade | max-age=31536000; includeSubDomains; preload |
X-Frame-Options | Clickjacking | DENY or SAMEORIGIN (use CSP frame-ancestors on modern stacks) |
X-Content-Type-Options | MIME sniffing attacks | nosniff |
Referrer-Policy | Referrer leakage of sensitive URLs | strict-origin-when-cross-origin |
Permissions-Policy | Abuse of browser APIs (camera, mic, geolocation) | Explicitly disable unused APIs |
X-XSS-Protection | Legacy browser XSS filter (deprecated) | 0 — disable it; use CSP instead |
Content-Security-Policy: The Hardest but Most Powerful
Content-Security-Policy (CSP) tells the browser which sources are trusted for scripts, styles, images, fonts, and other resource types. A correctly implemented CSP eliminates the entire family of reflected, stored, and DOM-based XSS attacks by preventing the browser from executing unauthorised scripts — even if the attacker has already injected the payload.
The directive that matters most is script-src. Without it, a single XSS payload can steal session cookies, exfiltrate form data, or inject malicious UI.
Rolling Out CSP Without Breaking Your Site
The biggest operational risk with CSP is accidentally blocking your own scripts. The safe rollout path:
- Start with
Content-Security-Policy-Report-Onlyand set areport-uriorreport-toendpoint. The browser enforces nothing but sends violation reports. Collect these for one to two weeks. - Eliminate
unsafe-inlineby replacing inline scripts with external.jsfiles, or by adding a cryptographic nonce ('nonce-<random>') or hash ('sha256-<hash>') to each inline block. Every inline<script>without a nonce or hash is a CSP bypass. - Move to enforcement mode by switching the header name to
Content-Security-Policy. - Add
upgrade-insecure-requeststo force mixed-content resources to HTTPS automatically.
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' is not a CSP. The unsafe-inline keyword nullifies the XSS protection entirely. Any VAPT scan will flag this as a Medium-to-High finding.script-src rather than falling back to unsafe-inline.graph TD
A[Browser receives HTTP response] --> B[Parse security headers]
B --> C{CSP present?}
C -->|Yes| D[Check script-src directive]
D --> E{Script origin allowed?}
E -->|Yes| F[Script executes]
E -->|No| G[Script blocked — violation reported]:::success
C -->|No| H[All scripts execute — XSS possible]:::danger
B --> I{HSTS present?}
I -->|Yes| J[Force HTTPS for max-age duration]:::success
I -->|No| K[HTTP allowed — SSL stripping possible]:::danger
B --> L{X-Frame-Options present?}
L -->|Yes| M[Framing blocked — clickjacking mitigated]:::success
L -->|No| N[Page can be framed — clickjacking possible]:::danger
classDef success fill:#1e3d2f,stroke:#10B981,color:#e2e8f0
classDef danger fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0Know your vulnerabilities before attackers do
Run a free VAPT scan — takes 5 minutes, no signup required.
Book Your Free ScanStrict-Transport-Security: Locking In HTTPS
HSTS tells browsers that your domain must only be contacted over HTTPS, for the duration specified in max-age. Once a browser has seen this header, it will internally redirect HTTP requests to HTTPS before they leave the machine — the attacker never gets to intercept the plain-text request.
Recommended value: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-age=31536000— one year; browser remembers the policy even after clearing cache.includeSubDomains— extends the policy to all subdomains. Required for preload submission.preload— signals your intent to be included in the HSTS preload list maintained by Google (used by all major browsers). Submit your domain at hstspreload.org.
preload until you are certain every subdomain is reachable over HTTPS. Preloading a domain that has HTTP-only subdomains will make those subdomains unreachable via browser. Removal from the preload list takes weeks.The attack HSTS defeats is SSL stripping, where a network-level attacker (on public Wi-Fi, a compromised router, or via ARP poisoning) downgrades your user's HTTPS connection to plain HTTP and reads session tokens and credentials in transit. Without HSTS, a valid HTTPS certificate alone does not prevent this.
X-Frame-Options and frame-ancestors: Stopping Clickjacking
Clickjacking loads your site invisibly inside an <iframe> on an attacker's page, then tricks the victim into clicking buttons on your site — authorising payments, changing passwords, approving transactions — without realising it.
X-Frame-Options: DENY prevents your page from being framed by any origin. SAMEORIGIN allows framing only from your own domain. For modern stacks, the CSP directive frame-ancestors 'none' or frame-ancestors 'self' achieves the same result with finer control and supersedes X-Frame-Options in browsers that support CSP Level 2. Include both for maximum compatibility.
X-Content-Type-Options: Blocking MIME Sniffing
Browsers historically tried to be "helpful" by guessing a file's content type even when the server declared otherwise — a behaviour called MIME sniffing. Attackers exploited this by uploading an image file containing JavaScript and getting old browsers to execute it.
X-Content-Type-Options: nosniff is a one-liner that instructs the browser to trust the Content-Type header and refuse to sniff. It is a trivial change with zero downside.
Referrer-Policy: Preventing URL Leakage
When a user navigates from your site to an external link, the browser sends the originating URL in the Referer header. If that URL contains a session token, a password-reset token, or a sensitive resource identifier, you have just handed it to a third-party server.
Referrer-Policy: strict-origin-when-cross-origin sends only the origin (not the path or query string) on cross-origin requests, while preserving the full referrer for same-origin navigation — the sensible default for most applications.
Permissions-Policy: Restricting Browser APIs
Permissions-Policy (formerly Feature-Policy) controls which browser APIs the page and its embedded iframes are allowed to access. An attacker who achieves XSS can use the camera, microphone, or geolocation APIs if the site has not explicitly restricted them.
Example for a site that needs none of these:
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()Each empty parenthesis means the feature is disabled for all origins, including iframes.
X-XSS-Protection: Deprecated — Disable It
The legacy X-XSS-Protection header enabled a reflective XSS filter in older IE and Chrome browsers. Modern browsers have removed the filter, and keeping the header set to 1; mode=block can actually introduce new vulnerabilities in some browser versions. The correct value today is X-XSS-Protection: 0 — explicitly disable it and rely on CSP for XSS mitigation.
How a VAPT Scan Grades Security Headers
Automated header scanning — as part of a VAPT or a standalone header check — evaluates each header against known-good configurations:
- Missing HSTS → Medium (potential SSL stripping; upgraded to High on login pages)
- Missing or weak CSP (e.g.,
unsafe-inlinepresent) → Medium to High - Missing X-Frame-Options / frame-ancestors → Medium (clickjacking risk)
- Missing X-Content-Type-Options → Low to Medium
- Missing Referrer-Policy → Low
- X-XSS-Protection set to 1 → Informational to Low (deprecated header present)
pie title Security Headers Most Commonly Missing - Illustrative Distribution
"CSP absent or unsafe-inline" : 40
"HSTS absent or no preload" : 25
"Referrer-Policy absent" : 15
"Permissions-Policy absent" : 12
"X-Content-Type-Options absent" : 8Common Mistakes to Avoid
- Setting headers only on the login page. Every page that loads authenticated content needs the full header set.
- Using
default-src *in CSP. A wildcard source allowlist defeats the purpose entirely. - Adding HSTS with a short
max-age(e.g., 300 seconds). This offers no real protection — attackers wait out short durations. - Skipping
includeSubDomainson HSTS when you have authentication on subdomains. An attacker can still strip HTTPS onapi.yourdomain.com. - Not testing after deployment. Use securityheaders.com or the OWASP Secure Headers Project scanner to verify your live header output before closing the ticket.
- Trusting framework defaults. Next.js, Django, and Rails have varying defaults — verify with a real HTTP response inspector, not just config files.
Putting It Together: A Deployment Checklist
| Step | Action | Tool |
|---|---|---|
| 1 | Audit current header output | curl -I https://yourdomain.com or securityheaders.com |
| 2 | Add HSTS without preload first | Nginx / Apache / Next.js headers() config |
| 3 | Deploy CSP-Report-Only with report endpoint | Collect violations for 1–2 weeks |
| 4 | Replace unsafe-inline with nonces/hashes | Developer task |
| 5 | Switch to enforcing CSP | Header name change |
| 6 | Add remaining headers | Single middleware/config block |
| 7 | Submit to HSTS preload list | hstspreload.org |
| 8 | Re-scan and verify | VAPT / securityheaders.com / Bachao.AI blog |
Authoritative References
- OWASP Secure Headers Project — canonical header guidance with grading criteria
- MDN Web Docs — HTTP Headers — per-header syntax, browser support, and directive reference
- CERT-In Annual Report 2023 — Indian incident statistics and web application threat landscape
- HSTS Preload Submission — official preload list maintained by Google Chrome
Frequently Asked Questions
What is the single most impactful HTTP security header to implement first?
Will adding a Content-Security-Policy break my website?
What does a VAPT report flag for missing security headers?
Is X-XSS-Protection still useful?
Do Indian businesses have a regulatory obligation to implement security headers?
How do I verify my security headers are actually being served correctly?
curl -I https://yourdomain.com to inspect the raw response headers. For a graded report, use securityheaders.com or the OWASP Secure Headers Project scanner. For a comprehensive check including all other vulnerability classes, run a full VAPT scan — header findings appear as a dedicated section in the report.