Quick Answer
Security headers are HTTP response headers that instruct browsers on how to handle your site's content. Without them, your app is vulnerable to clickjacking, cross-site scripting, protocol downgrade attacks, and information leakage. AI-generated apps built with Bolt, Lovable, v0, and Cursor almost universally ship with zero security headers - they are the single most common finding in vibe-coded web applications.
Why Vibe-Coded Apps Have No Security Headers
Security headers live in HTTP responses, not in application logic. They're set in server configuration, middleware, or framework config files - layers that AI tools either skip or treat as optional boilerplate. When Bolt scaffolds a Next.js app or Lovable generates a React frontend, it produces components and API routes. It does not produce a next.config.js with a complete security headers block.
The OWASP Foundation consistently lists missing security headers in its Web Security Testing Guide as low-effort, high-impact findings. According to the Mozilla Observatory, less than 30% of websites on the internet implement a Content Security Policy - and among newly deployed vibe-coded apps, the number is far lower. The 2024 Qualys Web Application Security Report found that missing Strict-Transport-Security headers affected 54% of scanned applications.
These headers cost almost nothing to add. A single configuration block can harden your entire application against multiple attack classes in under 10 minutes.
Before: A Typical Vibe-Coded App Response
Here is what an HTTP response from an AI-generated Next.js app deployed to Vercel looks like without any security header configuration:
$ curl -I https://your-vibe-app.vercel.app
HTTP/2 200
content-type: text/html; charset=utf-8
x-vercel-id: iad1::abc123
cache-control: private, no-cache, no-store, max-age=0, must-revalidate
# Missing headers:
# - Strict-Transport-Security (HSTS)
# - Content-Security-Policy
# - X-Frame-Options
# - X-Content-Type-Options
# - Referrer-Policy
# - Permissions-Policy
After: A Properly Configured Response
$ curl -I https://your-vibe-app.vercel.app
HTTP/2 200
content-type: text/html; charset=utf-8
strict-transport-security: max-age=63072000; includeSubDomains; preload
content-security-policy: default-src 'self'; script-src 'self' 'nonce-{nonce}'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://your-api.supabase.co
x-frame-options: DENY
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
What Each Header Does
| Header | Check ID | What It Prevents | Recommended Value |
|---|---|---|---|
| Strict-Transport-Security | HDR-001 | Protocol downgrade attacks, forces HTTPS | max-age=63072000; includeSubDomains; preload |
| Content-Security-Policy | HDR-002 | XSS, data injection, script hijacking | Start with default-src 'self' and expand |
| X-Frame-Options | HDR-003 | Clickjacking attacks via iframes | DENY or SAMEORIGIN |
| X-Content-Type-Options | HDR-004 | MIME-sniffing attacks | nosniff |
| Referrer-Policy | HDR-005 | Leaking URL paths to third parties | strict-origin-when-cross-origin |
| Permissions-Policy | HDR-006 | Unauthorized access to camera, mic, location | camera=(), microphone=(), geolocation=() |
Adding Security Headers in Next.js
For Next.js apps - the most common target for Bolt, v0, and Cursor - security headers are added in next.config.js. This applies them to every response from your application:
// ✅ GOOD - next.config.js with security headers
const securityHeaders = [
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload',
},
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=()',
},
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-eval' 'unsafe-inline'", // tighten after testing
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
"img-src 'self' data: https:",
"font-src 'self' https://fonts.gstatic.com",
"connect-src 'self'",
].join('; '),
},
];
/** @type {import('next').NextConfig} */
const nextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: securityHeaders,
},
];
},
};
module.exports = nextConfig;
Content Security Policy: Start Simple, Tighten Over Time
CSP is the most powerful header on the list, and also the most complex. A misconfigured CSP can break your application by blocking legitimate scripts, stylesheets, or API calls. The safe approach is to start permissive and tighten iteratively.
Use CSP report-only mode first: set Content-Security-Policy-Report-Only instead of Content-Security-Policy. This logs violations without enforcing them. Review the violation reports in your browser DevTools console or a reporting endpoint, then tighten the policy until violations stop.
For apps using Google Fonts (common in Lovable-generated designs), Supabase API calls, and Vercel Analytics, your CSP connect-src and font-src directives will need to explicitly allow those domains. The table above shows which sources are commonly needed.
How to Find Missing Headers in Your App
Tools like VibeDoctor (vibedoctor.io) automatically scan your deployed web app for missing security headers and flag which of HDR-001 through HDR-006 are absent from your HTTP responses. Free to sign up.
You can also check manually using the Mozilla Observatory at observatory.mozilla.org, SecurityHeaders.com, or by running curl -I your-domain.com and reviewing the response headers. Any production app scoring below B on the Mozilla Observatory has fixable issues that can be resolved in under an hour.
FAQ
Will adding a strict CSP break my Bolt or Lovable app?
Possibly, initially. Vibe-coded apps often use inline styles and scripts that CSP blocks by default. Start with Content-Security-Policy-Report-Only to identify violations, then either refactor inline code or add specific allowances. The unsafe-inline allowance for styles is a common concession for AI-generated apps using component libraries.
Does Vercel add any security headers automatically?
Vercel adds a few headers by default (like X-Vercel-ID) but does not automatically add security-critical headers like HSTS, CSP, or X-Frame-Options. You must configure them explicitly in next.config.js or vercel.json. Vercel's documentation recommends the next.config.js headers() approach for Next.js apps.
What is clickjacking and how does X-Frame-Options prevent it?
Clickjacking embeds your site in an invisible iframe on an attacker's page, tricking users into clicking buttons they cannot see. X-Frame-Options: DENY prevents any site from embedding your pages in a frame. The modern equivalent is CSP frame-ancestors 'none', which overrides X-Frame-Options when both are present.
Is HSTS safe to add immediately?
Only if your site is already fully on HTTPS. HSTS tells browsers to never connect over HTTP again for the specified duration. If you add it while any part of your site is still accessible over HTTP, you may lock out users. Start with a short max-age (e.g., 300 seconds) and increase it to 63072000 (2 years) once you are confident.
Do these headers need to be set on API routes too?
For API responses, HSTS and X-Content-Type-Options are valuable. CSP and X-Frame-Options are less relevant for JSON API endpoints but harmless to include. The Next.js headers() configuration applies to all routes matching the source pattern, so using source: '/(.*))' covers both pages and API routes uniformly.