Missing Security Headers
Security headers are short instructions your server sends with every page. They tell the browser which scripts may run, to always use HTTPS, and whether other sites may frame your app. They cost nothing to add and nothing in your UI changes. On AI-built apps, they're the most common gap we see.
In our passive check of 18 public showcase apps built with Lovable or Bolt, 18 of 18 had no Content-Security-Policy and 18 of 18 had no clickjacking protection. 4 of 18 were missing X-Content-Type-Options and 4 of 18 were missing Referrer-Policy. The average score was 76 out of 100.
Run the free security headers check to see exactly which headers your app sends.
The checklist
1. Content-Security-Policy (CSP) — high priority
CSP lists the sources your page is allowed to load scripts, styles, images and connections from. If someone manages to inject a script through a comment field, a compromised package or a bad URL parameter, a good CSP stops the browser from running it or sending data to an attacker's server. MDN describes CSP as a main defense against cross-site scripting.
A starting point for a typical Vite + Supabase app:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://YOUR-PROJECT.supabase.co wss://YOUR-PROJECT.supabase.co; frame-ancestors 'self'; base-uri 'self'; form-action 'self'
Add your real third parties (fonts, analytics, Stripe) as you find them. Roll it out safely: send Content-Security-Policy-Report-Only with the same value first, use the app, check the console for violations, then switch to the enforcing header. Avoid script-src * and 'unsafe-eval'. Next.js apps usually need a nonce-based policy for inline scripts.
2. Clickjacking protection — medium
Without frame-ancestors in your CSP or an X-Frame-Options header, another site can load your app in an invisible iframe and trick users into clicking real buttons. Use frame-ancestors 'self' (or 'none') in CSP, and add X-Frame-Options: DENY or SAMEORIGIN for older browsers. Per MDN, neither works in a <meta> tag. They have to be real HTTP headers.
3. Strict-Transport-Security (HSTS) — medium
HSTS tells browsers to use HTTPS for your domain, even if someone types http:// or clicks an old link:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Only add includeSubDomains if every subdomain serves HTTPS. Don't add preload until you understand that it's hard to undo.
4. X-Content-Type-Options — low
X-Content-Type-Options: nosniff stops browsers from guessing a file's type. Guessing can turn an uploaded "image" into a running script.
5. Referrer-Policy — low
Referrer-Policy: strict-origin-when-cross-origin sends only your domain, not the full URL, to other sites. Full URLs can include reset tokens or search terms.
6. Permissions-Policy — informational
Permissions-Policy: camera=(), microphone=(), geolocation=() turns off powerful browser features you don't use. It's optional, and we don't deduct points for it.
Where to add headers (by host)
Headers come from whatever serves your files, not from your React code.
- Netlify (most Bolt apps that publish to Netlify): a
_headersfile inpublic/or[[headers]]innetlify.toml. Netlify doesn't apply these to functions, edge functions or proxied responses. - Vercel (v0, many Cursor apps):
headers()innext.config.js, orheadersinvercel.json. - Replit: middleware such as
helmet()for Express, or[[deployment.responseHeaders]]in.replitfor static deployments. - Lovable hosting: the publishing docs don't describe a custom header setting. You can add a CSP
<meta>tag (partial: noframe-ancestors), set headers at a CDN in front of your custom domain, or deploy the GitHub-synced build to a host that reads a headers file. - Express/Node:
app.use(helmet()), then tune the CSP. - Nginx:
add_header Content-Security-Policy "…" always;and so on.
Verify: run curl -I https://your-domain and look for each header by name, or rescan with our tool.
What else the check covers
Headers are one of six areas in the Pro Logica AI App Security Check. The others are exposed keys, Supabase and Firebase access, public /.env and /.git, source maps, and Google indexability. We also flag Access-Control-Allow-Origin: *, which is critical when it's paired with credentials, and certificates that are invalid or about to expire.
Related
- By builder: Bolt.new · v0 · Lovable · Replit · Cursor
- Exposed API key in frontend
- Next step: Run the free check on your app · Pro Logica cybersecurity services
FAQ
Which security header matters most?
Content-Security-Policy, because it limits the damage from injected scripts. After that, clickjacking protection and HSTS. All of them take minutes to add once you know where your host reads headers.
Will adding CSP break my site?
A strict policy can block fonts, analytics or your database connection if you leave them out. That's why you start with Content-Security-Policy-Report-Only, fix the violations, and then enforce.
Can I set headers with a <meta> tag?
Only part of CSP. Browsers ignore frame-ancestors, report-uri and sandbox in a meta CSP, and X-Frame-Options and HSTS don't work as meta tags at all. Use real HTTP headers whenever you can.
Do security headers help SEO?
They aren't a ranking factor we can point to. HTTPS is, though, and a site that looks trustworthy keeps visitors who would otherwise bounce. The same scan also checks the things that do block Google: noindex, titles, meta descriptions and sitemaps.
How do I know my headers are working?
Run curl -I against your production URL, or rescan. Each fixed header drops off your report and your score goes up.
See which headers your app is missing. Run the free AI app security check.
By Pro Logica