10 HTTP Security Headers Every App Should Have
A clear guide to the essential HTTP security headers: what they do, the risk you run if they're missing, and how to fix your web security headers with AI.
You launched your app, it works, users are signing in. But there's a layer of security almost nobody checks until something goes wrong: HTTP security headers. These are instructions your server sends to the browser with every response, telling it how to behave so it protects both you and your users.
The good news: setting them up doesn't require rewriting your app. It's usually a matter of adding a few lines in your server, your framework, or your CDN. The bad news: when they're missing, you leave the door open to attacks you won't even notice until an attacker takes advantage of them.
In this guide we'll walk through ten essential web security headers, one by one: what each one does, what you risk if it's absent, what a good configuration looks like, how to verify it, and the idea of asking your AI to do the fix for you (Claude, Cursor, whatever you use). No extra jargon, plain and direct as always.
Note: the values we show are reasonable examples to use as a starting point. Adapt them to your app and test before pushing to production, especially with Content-Security-Policy.
1. Content-Security-Policy (CSP)
What it does. It tells the browser which origins it's allowed to load scripts, styles, images, and other resources from. It's your best defense against Cross-Site Scripting (XSS) attacks: even if someone manages to inject a malicious <script>, the browser refuses to run it unless it's on your approved list.
Risk if it's missing. Without CSP, any injected script runs with full permissions. An XSS attack can steal your users' sessions, tokens, and data.
What good looks like. A strict policy that avoids unsafe-inline whenever you can:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
How to verify it. Open your browser's DevTools (Network tab), check your page's response headers, or look it up in your Pursecure report.
Ask your AI. "Generate a strict Content-Security-Policy for my Next.js app that uses only my own resources and Google Fonts, with no unsafe-inline, and tell me how to add it in my configuration."
2. Strict-Transport-Security (HSTS)
What it does. It forces the browser to connect to your site over HTTPS only for a defined period of time. Once the browser sees this header, it won't even try to use HTTP, even if the user types http://.
Risk if it's missing. An attacker on the same network (a public Wi-Fi, for example) can intercept that first HTTP connection and run a man-in-the-middle attack before you ever reach HTTPS.
What good looks like.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
That's one year, applied to subdomains. Add preload only when you're sure your entire domain serves HTTPS.
How to verify it. Check the headers on your site served over HTTPS. If max-age is very low or the header doesn't show up at all, there's work to do.
Ask your AI. "Configure HSTS with a one-year duration and includeSubDomains on my [your stack] server, and explain the risk of enabling preload too early."
3. X-Content-Type-Options
What it does. With the value nosniff, it tells the browser not to guess a file's type. It respects the Content-Type you declare and nothing else.
Risk if it's missing. The browser could interpret a file uploaded by a user (one you think is harmless) as an executable script. That enables MIME sniffing attacks.
What good looks like.
X-Content-Type-Options: nosniff
There are no nuances here: either it's set or it isn't. Always set it.
How to verify it. Check that the header appears in your app's responses.
Ask your AI. "Add the X-Content-Type-Options: nosniff header to all of my app's responses in [your framework]."
4. X-Frame-Options
What it does. It controls whether your site can be displayed inside an <iframe> on another page. It prevents clickjacking, where an attacker embeds your site in a fake page and tricks the user into clicking without realizing it.
Risk if it's missing. Your login screen or a "confirm payment" button can be invisibly overlaid on a malicious site. The user thinks they're clicking on one thing while they're actually acting on yours.
What good looks like.
X-Frame-Options: DENY
For modern apps, the frame-ancestors 'none' option inside your CSP does the same job and is more flexible. Having both doesn't hurt.
How to verify it. Confirm the header is present, or that your CSP includes frame-ancestors.
Ask your AI. "Protect my app against clickjacking with X-Frame-Options: DENY and the frame-ancestors directive in my CSP."
5. Referrer-Policy
What it does. It controls how much information about the originating URL gets sent when a user clicks a link that leaves your site. Without control, you could be leaking internal paths, IDs, or tokens that travel in the URL.
Risk if it's missing. External sites and third-party analytics tools receive your full URLs in the Referer header. That can expose sensitive data you never meant to share.
What good looks like.
Referrer-Policy: strict-origin-when-cross-origin
It sends the full URL within your own domain, but only the origin (without the path) when the destination is external.
How to verify it. Check the header in your responses; if it doesn't appear, the browser uses its default value, which isn't always the most cautious one.
Ask your AI. "Set Referrer-Policy: strict-origin-when-cross-origin in my app and explain what data stops being leaked."
6. Permissions-Policy
What it does. It decides which browser features your site can use: camera, microphone, geolocation, sensors. If your app doesn't need the microphone, you turn it off, and in doing so you also turn it off for any third-party script or embedded iframe.
Risk if it's missing. A compromised script or a third-party iframe could request access to the user's camera or location on your behalf.
What good looks like. Deny everything you don't use:
Permissions-Policy: camera=(), microphone=(), geolocation=(), browsing-topics=()
If you need a particular feature, enable it only for your own origin, for example geolocation=(self).
How to verify it. Confirm the header in your responses and check that it only allows what your app actually needs.
Ask your AI. "Generate a Permissions-Policy header that disables camera, microphone, and geolocation for my app, which doesn't use any of those features."
7. Cookies with Secure, HttpOnly, and SameSite
What it does. This isn't a general response header but three attributes you set on each sensitive cookie (like your session cookie):
- Secure: the cookie only travels over HTTPS.
- HttpOnly: JavaScript can't read it, so an XSS attack can't steal your session.
- SameSite: limits when the cookie is sent on requests from other sites; helps against CSRF.
Risk if it's missing. Without HttpOnly, an XSS attack can read the session cookie and impersonate the user. Without Secure, the cookie can travel in the clear. Without SameSite, you're exposed to CSRF attacks.
What good looks like.
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/
Use SameSite=Strict if your flow allows it; Lax is a good balance for most cases.
How to verify it. In DevTools, on the Application/Storage tab, check that your session cookies have all three flags.
Ask your AI. "Make sure my [your stack] app's session cookies have Secure, HttpOnly, and SameSite, and show me the exact change."
8. Properly Configured CORS
What it does. CORS (Cross-Origin Resource Sharing) defines which external origins can call your API from the browser. It's the opposite of a blocking header: if you misconfigure it and leave it too open, you're giving anyone permission.
Risk if it's missing or wrong. The classic mistake is responding with Access-Control-Allow-Origin: * together with Access-Control-Allow-Credentials: true, or reflecting back whatever origin shows up. That lets malicious sites make authenticated requests on behalf of your users.
What good looks like. An explicit list of trusted origins, never a wildcard when you're handling credentials:
Access-Control-Allow-Origin: https://app.yourdomain.com
Access-Control-Allow-Credentials: true
Validate the origin against an allowlist in your backend and respond only to the ones you approve.
How to verify it. Make a request from a different origin and observe what Access-Control-Allow-Origin your API responds with. If it reflects back any origin, there's a problem.
Ask your AI. "Review my CORS configuration and change it so it only allows an allowlist of origins, with no wildcard when I'm using credentials."
9. Open Redirects
What it does. This isn't a header, but it's such a common web security problem that it deserves a spot. It happens when your app redirects to a URL that comes from an unvalidated parameter, for example ?next=https://malicious-site.com.
Risk if there's no control. An attacker sends a link that starts on your domain (so it looks trustworthy) and ends up taking the user to a phishing site. Your brand provides the trust, the attacker provides the trap.
What good looks like. Never redirect to an arbitrary user-supplied URL. Validate against internal routes or a list of allowed destinations:
// Instead of redirecting to req.query.next directly:
const allowedDestinations = ["/dashboard", "/profile"];
const destination = allowedDestinations.includes(next) ? next : "/dashboard";
How to verify it. Search your code for where you read parameters like next, redirect, url, return_to and confirm that each one is validated before it's used in a redirect.
Ask your AI. "Find open redirects in my app: anywhere I redirect to a URL that comes from the request without validating it, and fix them with an allowlist."
10. X-XSS-Protection and the "Defense in Depth" Mindset
What it does. The old X-XSS-Protection header used to enable a browser filter against XSS. Today, modern browsers ignore it or no longer support it, and the recommendation is to rely on a solid CSP (header number 1). We mention it so you know that seeing it in older guides doesn't mean you should depend on it.
The bigger idea. No single header saves you on its own. Real security comes from combining them: CSP stops XSS, HSTS secures the channel, cookie flags protect the session, CORS controls who calls your API. It's defense in depth.
What good looks like. If you use it for compatibility, leave it turned off to avoid odd behavior:
X-XSS-Protection: 0
And put your energy into a strong CSP.
Ask your AI. "Explain why X-XSS-Protection is deprecated and help me replace its protection with a well-built CSP."
What to Check on Your Site Today
If you only have ten minutes, do this:
- Open DevTools (Network tab), reload your site, and click the first request. Look at the list of Response Headers.
- Look for the must-haves:
Content-Security-Policy,Strict-Transport-Security,X-Content-Type-Options,X-Frame-Options,Referrer-Policy, andPermissions-Policy. Note which ones are missing. - Check your cookies on the Application/Storage tab: does your session cookie have
Secure,HttpOnly, andSameSite? - Test your API from another origin and observe what it responds with for CORS. Be suspicious of any wildcard alongside credentials.
- Search your code for redirect parameters (
next,redirect,url) and confirm they're validated.
For every gap you find, copy one of the prompts from this guide and hand it to your AI along with the name of your stack. You'll be surprised how fast they close.
Wrap-up: Let Pursecure Take the Inventory for You
Checking headers by hand works, but it's easy to forget one or misread a value. That's exactly why Pursecure exists: you paste in your site's URL and within minutes you get a score from 0 to 100 with every finding sorted by severity, including each of the HTTP security headers in this guide.
And the best part: every finding comes with a ready-to-use prompt to fix it with AI. You don't have to guess how to configure your CSP or your cookies: you copy the prompt, paste it into Claude or Cursor, and apply the change. No alarmism and no extra theory, straight to the solution.
Scan your site for free at pursecure.app and find out which of these ten web security headers you're missing today.
Check your site's security for free
Paste your URL and in seconds you'll see what your app is exposing, with the prompt ready to fix it with your AI.
Scan for free