Guides · VibeVigil
How to Add Security Headers to Your Lovable, Bolt, v0 or Cursor App
What security headers are, how to check if your AI-built app has them, and copy-paste setups for Vercel, Netlify, Cloudflare Pages and Next.js, plus a prompt for your coding agent.
A security scanner told you your app is "missing security headers." Or you just want to know what that means. This guide explains it in plain words, shows you how to check your own app, and gives you setups you can copy.
You don't need to be a developer.
The short answer
- Security headers are small notes your server sends with every page. They tell the browser what it is allowed to do with your site.
- They don't fix a leaky database or an exposed key. They do block some common attacks, like other sites loading your app inside a hidden frame, or the browser running scripts it shouldn't.
- Most AI app builders don't add them for you. You add them once, where your app is hosted.
- Start with the five "safe" headers below. Add a Content Security Policy last, and test it first, because a wrong one can break your app.
What each header does
| Header | What it does | Safe starting value |
|---|---|---|
Strict-Transport-Security | Tells browsers to always use HTTPS for your site | max-age=31536000; includeSubDomains |
X-Content-Type-Options | Stops the browser from guessing file types (a trick attackers use) | nosniff |
X-Frame-Options | Stops other sites from showing your app inside a frame (clickjacking) | DENY |
Referrer-Policy | Limits what URL info is shared when users click a link to another site | strict-origin-when-cross-origin |
Permissions-Policy | Turns off browser features you don't use, like the camera or microphone | camera=(), microphone=(), geolocation=() |
Content-Security-Policy | Lists where scripts, styles, images and data may load from | Depends on your app (see Step 4) |
You can read the official details for each one on MDN and in the OWASP Secure Headers Project.
Careful with two of them:
- If your app uses the camera, microphone or location, don't block that feature in
Permissions-Policy. Usecamera=(self)instead ofcamera=(). - If your app is meant to be shown inside another site (for example, an embeddable widget), don't use
X-Frame-Options: DENY. Use theframe-ancestorsrule in your Content Security Policy to list the sites that may embed it.
Step 1: Check what your app sends today
Option A: In your browser (no tools needed)
- Open your live app in Chrome, Edge or Firefox.
- Right-click the page and choose Inspect, then open the Network tab.
- Reload the page. Click the first row (your page's address).
- Look under Response Headers. Do you see the headers from the table above?
Option B: In a terminal
curl -sI https://your-app.comThis prints the headers your server sends. Look for the names in the table.
Option C: A free public checker
Mozilla's HTTP Observatory grades your headers and explains each missing one. Only check sites you own or have permission to test.
Step 2: Find where your app is hosted
Headers are set by the host, not by the page code. So the fix depends on where your app lives:
- Vercel (common for v0 and Cursor apps): use
vercel.json, ornext.config.jsfor Next.js apps. - Netlify (used by many Bolt and Vite apps): use a
_headersfile ornetlify.toml. - Cloudflare Pages: use a
_headersfile. - Your AI builder's own hosting (for example, a
lovable.apporbolt.hostaddress): check that builder's docs or settings to see if you can set custom headers. If you can't, you can deploy the same app to a host that allows it, or put a custom domain behind a service that can add headers.
Not sure? Look at the address your app runs on, or ask your coding agent: "Where is this app deployed, and how do I set HTTP response headers there?"
Step 3: Add the safe headers
Pick the one setup that matches your host. All of these add the same five headers to every page.
Vercel (any framework): vercel.json
Put this file in the root of your project. See Vercel's docs.
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
]
}
]
}If you already have a vercel.json, add the headers part to it. Don't make a second file.
Next.js: next.config.js
See the Next.js headers docs.
const securityHeaders = [
{ key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
];
module.exports = {
async headers() {
return [{ source: '/:path*', headers: securityHeaders }];
},
};If your config file already has other settings, merge the headers() function into it.
Netlify or Cloudflare Pages: _headers
Put a file named _headers (no file extension) in the folder that gets published. For Vite apps that is usually public/, so it gets copied into dist/. See Netlify's docs and Cloudflare's docs.
/*
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()Deploy, then repeat Step 1 to confirm the headers show up.
Step 4: Add a Content Security Policy (carefully)
A Content Security Policy (CSP) is the strongest header and the easiest to get wrong. It lists every place your app may load code and data from. If you forget one (say, Supabase, Stripe or your analytics), that part of your app stops working.
So do it in two steps:
1. Start in "report only" mode. This header warns you in the browser console but blocks nothing:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://YOUR-PROJECT.supabase.co wss://YOUR-PROJECT.supabase.co; frame-ancestors 'none'; base-uri 'self'; form-action 'self'Replace YOUR-PROJECT with your Supabase project ID. Add any other services your app talks to (for example, https://js.stripe.com in script-src, and Stripe's domains in frame-src).
2. Use the app for a few minutes. Sign in, upload, pay, and open every main page. Then open the browser console and look for CSP warnings. Each warning names a blocked address. Add the ones you trust to the policy.
When there are no more warnings, rename the header from Content-Security-Policy-Report-Only to Content-Security-Policy. Now it is enforced.
You can read the full rules on MDN's Content-Security-Policy page.
Common mistakes
- Adding headers in the HTML instead of the server. Most of these headers only work when the server sends them. A
<meta>tag can't setX-Frame-OptionsorStrict-Transport-Security. - Turning on HSTS before HTTPS works everywhere.
Strict-Transport-Securitymakes browsers refuse plain HTTP for a long time. Make sure every subdomain has a working certificate first. If some don't, leave outincludeSubDomains. - Copying a strict CSP from someone else. Every app loads from different places. A copied policy will likely break yours.
- Thinking headers make the app "secure." Headers are one layer. Your database rules (RLS) and keeping secret keys out of the browser matter more. See our guides on checking Supabase RLS and finding leaked API keys.
Prompt for your coding agent
Paste this into Lovable, Bolt, v0, Cursor or a similar tool:
Add HTTP security headers to this app.
1. Tell me where the app is deployed and which file sets response headers there (vercel.json, next.config.js, a _headers file, netlify.toml, or other). If the current host can't set custom headers, tell me plainly and stop.
2. Add these headers to every route:
- Strict-Transport-Security: max-age=31536000; includeSubDomains
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY (skip this and tell me if the app is meant to be embedded in other sites)
- Referrer-Policy: strict-origin-when-cross-origin
- Permissions-Policy: camera=(), microphone=(), geolocation=() (allow with (self) any feature the app actually uses)
3. Add a Content-Security-Policy-Report-Only header (NOT enforced yet). Build it from the services this app really uses: list every external domain for scripts, styles, images, fonts, API calls (including Supabase https and wss), and frames.
4. Don't remove or change any existing headers or config without telling me.
5. After deploying, show me the command to check the headers (curl -sI <my-url>) and what I should see.Quick checklist
- [ ] I checked my live app's response headers.
- [ ] I know where my app is hosted and where headers are set.
- [ ] HSTS, nosniff, frame protection, referrer policy and permissions policy are on.
- [ ] A Content Security Policy runs in report-only mode, and I fixed the warnings.
- [ ] After testing, the CSP is enforced.
- [ ] I also checked RLS and exposed keys, because headers alone don't protect data.
Scan your app free. It checks the public page of a site you own. No account.