[{"data":1,"prerenderedAt":474},["ShallowReactive",2],{"blog-best-practices/headers":3},{"id":4,"title":5,"body":6,"category":450,"date":451,"dateModified":451,"description":452,"draft":453,"extension":454,"faq":455,"featured":453,"headerVariant":459,"image":460,"keywords":460,"meta":461,"navigation":462,"ogDescription":463,"ogTitle":460,"path":464,"readTime":465,"schemaOrg":466,"schemaType":467,"seo":468,"sitemap":469,"stem":470,"tags":471,"twitterCard":472,"__hash__":473},"blog/blog/best-practices/headers.md","Security Headers Best Practices: CSP, HSTS, X-Frame-Options",{"type":7,"value":8,"toc":429},"minimark",[9,16,25,30,33,48,52,57,60,69,79,83,86,95,184,188,191,200,204,207,216,220,223,232,236,239,248,252,256,265,269,278,282,291,295,298,326,349,371,375,378,398,417],[10,11,12],"tldr",{},[13,14,15],"p",{},"The #1 security headers best practice is to add defense-in-depth headers that protect against XSS, clickjacking, and protocol downgrade attacks. Start with X-Content-Type-Options, X-Frame-Options, and Strict-Transport-Security. Add Content-Security-Policy for best protection. Test with securityheaders.com.",[17,18,19],"quotable-box",{},[20,21,22],"blockquote",{},[13,23,24],{},"\"Security headers are your first line of defense. They cost nothing to implement but protect against entire classes of attacks.\"",[26,27,29],"h2",{"id":28},"essential-security-headers","Essential Security Headers",[13,31,32],{},"Every web application should include these headers:",[34,35,37],"code-block",{"label":36},"Essential headers (copy-paste ready)",[38,39,44],"pre",{"className":40,"code":42,"language":43},[41],"language-text","X-Content-Type-Options: nosniff\nX-Frame-Options: DENY\nX-XSS-Protection: 1; mode=block\nReferrer-Policy: strict-origin-when-cross-origin\nPermissions-Policy: camera=(), microphone=(), geolocation=()\nStrict-Transport-Security: max-age=31536000; includeSubDomains\n","text",[45,46,42],"code",{"__ignoreMap":47},"",[26,49,51],{"id":50},"header-by-header-explanation","Header-by-Header Explanation",[53,54,56],"h3",{"id":55},"strict-transport-security-hsts-2-min","Strict-Transport-Security (HSTS) 2 min",[13,58,59],{},"Forces browsers to use HTTPS, preventing protocol downgrade attacks:",[34,61,63],{"label":62},"HSTS configuration",[38,64,67],{"className":65,"code":66,"language":43},[41],"Strict-Transport-Security: max-age=31536000; includeSubDomains\n\n// Options:\n// max-age: Time in seconds to remember HTTPS-only (1 year recommended)\n// includeSubDomains: Apply to all subdomains\n// preload: Submit to browser preload list (permanent, use carefully)\n",[45,68,66],{"__ignoreMap":47},[70,71,72],"warning-box",{},[13,73,74,78],{},[75,76,77],"strong",{},"HSTS is sticky."," Once set, browsers will refuse HTTP connections for the max-age period. Start with a short max-age (3600) to test, then increase to 31536000 (1 year).",[53,80,82],{"id":81},"content-security-policy-csp-5-min","Content-Security-Policy (CSP) 5 min",[13,84,85],{},"The most powerful header for preventing XSS:",[34,87,89],{"label":88},"Basic CSP",[38,90,93],{"className":91,"code":92,"language":43},[41],"Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.yourdomain.com; frame-ancestors 'none'\n",[45,94,92],{"__ignoreMap":47},[96,97,98,114],"table",{},[99,100,101],"thead",{},[102,103,104,108,111],"tr",{},[105,106,107],"th",{},"Directive",[105,109,110],{},"Controls",[105,112,113],{},"Recommended Value",[115,116,117,129,140,151,162,173],"tbody",{},[102,118,119,123,126],{},[120,121,122],"td",{},"default-src",[120,124,125],{},"Fallback for all resource types",[120,127,128],{},"'self'",[102,130,131,134,137],{},[120,132,133],{},"script-src",[120,135,136],{},"JavaScript sources",[120,138,139],{},"'self' (avoid 'unsafe-inline')",[102,141,142,145,148],{},[120,143,144],{},"style-src",[120,146,147],{},"CSS sources",[120,149,150],{},"'self' 'unsafe-inline'",[102,152,153,156,159],{},[120,154,155],{},"img-src",[120,157,158],{},"Image sources",[120,160,161],{},"'self' data: https:",[102,163,164,167,170],{},[120,165,166],{},"connect-src",[120,168,169],{},"Fetch, XHR, WebSocket",[120,171,172],{},"'self' your-api",[102,174,175,178,181],{},[120,176,177],{},"frame-ancestors",[120,179,180],{},"Who can embed you",[120,182,183],{},"'none' or 'self'",[53,185,187],{"id":186},"x-frame-options-1-min","X-Frame-Options 1 min",[13,189,190],{},"Prevents clickjacking by controlling if your site can be embedded:",[34,192,194],{"label":193},"X-Frame-Options values",[38,195,198],{"className":196,"code":197,"language":43},[41],"X-Frame-Options: DENY           // Cannot be embedded anywhere\nX-Frame-Options: SAMEORIGIN     // Only same origin can embed\nX-Frame-Options: ALLOW-FROM uri // Deprecated, use CSP instead\n",[45,199,197],{"__ignoreMap":47},[53,201,203],{"id":202},"x-content-type-options-1-min","X-Content-Type-Options 1 min",[13,205,206],{},"Prevents MIME type sniffing attacks:",[34,208,210],{"label":209},"MIME sniffing prevention",[38,211,214],{"className":212,"code":213,"language":43},[41],"X-Content-Type-Options: nosniff\n\n// Without this, browsers might execute a file as JavaScript\n// even if served with a different Content-Type\n",[45,215,213],{"__ignoreMap":47},[53,217,219],{"id":218},"referrer-policy-1-min","Referrer-Policy 1 min",[13,221,222],{},"Controls what referrer information is sent with requests:",[34,224,226],{"label":225},"Referrer-Policy options",[38,227,230],{"className":228,"code":229,"language":43},[41],"Referrer-Policy: no-referrer                    // Never send referrer\nReferrer-Policy: strict-origin-when-cross-origin // Recommended\nReferrer-Policy: same-origin                     // Only for same-origin\n",[45,231,229],{"__ignoreMap":47},[53,233,235],{"id":234},"permissions-policy-1-min","Permissions-Policy 1 min",[13,237,238],{},"Controls which browser features your site can use:",[34,240,242],{"label":241},"Permissions-Policy configuration",[38,243,246],{"className":244,"code":245,"language":43},[41],"Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()\n\n// Disable features you don't use to reduce attack surface\n// () = disabled, (self) = this origin only, (*) = any origin\n",[45,247,245],{"__ignoreMap":47},[26,249,251],{"id":250},"framework-configuration","Framework Configuration",[53,253,255],{"id":254},"nextjs-2-min","Next.js 2 min",[34,257,259],{"label":258},"next.config.js",[38,260,263],{"className":261,"code":262,"language":43},[41],"module.exports = {\n  async headers() {\n    return [\n      {\n        source: '/(.*)',\n        headers: [\n          { key: 'X-Content-Type-Options', value: 'nosniff' },\n          { key: 'X-Frame-Options', value: 'DENY' },\n          { key: 'X-XSS-Protection', value: '1; mode=block' },\n          { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },\n          { key: 'Permissions-Policy', value: 'camera=(), microphone=()' },\n        ],\n      },\n    ];\n  },\n};\n",[45,264,262],{"__ignoreMap":47},[53,266,268],{"id":267},"express-2-min","Express 2 min",[34,270,272],{"label":271},"Express with Helmet",[38,273,276],{"className":274,"code":275,"language":43},[41],"import helmet from 'helmet';\n\napp.use(helmet({\n  contentSecurityPolicy: {\n    directives: {\n      defaultSrc: [\"'self'\"],\n      scriptSrc: [\"'self'\"],\n      styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n      imgSrc: [\"'self'\", \"data:\", \"https:\"],\n    },\n  },\n  hsts: {\n    maxAge: 31536000,\n    includeSubDomains: true,\n  },\n}));\n",[45,277,275],{"__ignoreMap":47},[53,279,281],{"id":280},"vercelnetlify-1-min","Vercel/Netlify 1 min",[34,283,285],{"label":284},"vercel.json",[38,286,289],{"className":287,"code":288,"language":43},[41],"{\n  \"headers\": [\n    {\n      \"source\": \"/(.*)\",\n      \"headers\": [\n        { \"key\": \"X-Content-Type-Options\", \"value\": \"nosniff\" },\n        { \"key\": \"X-Frame-Options\", \"value\": \"DENY\" },\n        { \"key\": \"Referrer-Policy\", \"value\": \"strict-origin-when-cross-origin\" }\n      ]\n    }\n  ]\n}\n",[45,290,288],{"__ignoreMap":47},[26,292,294],{"id":293},"testing-your-headers","Testing Your Headers",[13,296,297],{},"Use these tools to verify your configuration:",[299,300,301,308,314,320],"ul",{},[302,303,304,307],"li",{},[75,305,306],{},"securityheaders.com:"," Comprehensive header analysis",[302,309,310,313],{},[75,311,312],{},"Mozilla Observatory:"," Full security audit",[302,315,316,319],{},[75,317,318],{},"Browser DevTools:"," Network tab shows response headers",[302,321,322,325],{},[75,323,324],{},"curl -I:"," Quick header check from terminal",[327,328,329],"info-box",{},[13,330,331,334,335,342,343,348],{},[75,332,333],{},"Learn More:"," For comprehensive documentation on security headers, see ",[336,337,341],"a",{"href":338,"rel":339},"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#security",[340],"nofollow","MDN Web Docs: HTTP Security Headers"," and ",[336,344,347],{"href":345,"rel":346},"https://owasp.org/www-project-secure-headers/",[340],"OWASP Secure Headers Project",".",[350,351,352,359,365],"faq-section",{},[353,354,356],"faq-item",{"question":355},"Do I need both X-Frame-Options and frame-ancestors?",[13,357,358],{},"For maximum compatibility, use both. CSP frame-ancestors is more flexible and overrides X-Frame-Options in modern browsers, but X-Frame-Options supports older browsers.",[353,360,362],{"question":361},"Why does CSP break my site?",[13,363,364],{},"CSP blocks resources that do not match your policy. Start with report-only mode (Content-Security-Policy-Report-Only) to find violations without breaking anything, then fix and switch to enforcing mode.",[353,366,368],{"question":367},"Should I use X-XSS-Protection?",[13,369,370],{},"Modern browsers have deprecated it, and it can introduce vulnerabilities in older browsers. Include it for legacy support but rely on CSP for real protection.",[26,372,374],{"id":373},"further-reading","Further Reading",[13,376,377],{},"Put these practices into action with our step-by-step guides.",[299,379,380,386,392],{},[302,381,382],{},[336,383,385],{"href":384},"/blog/how-to/add-security-headers","Add security headers to your app",[302,387,388],{},[336,389,391],{"href":390},"/blog/checklists/pre-deployment-security-checklist","Pre-deployment security checklist",[302,393,394],{},[336,395,397],{"href":396},"/blog/getting-started/first-scan","Run your first security scan",[399,400,401,407,412],"related-articles",{},[402,403],"related-card",{"description":404,"href":405,"title":406},"Cross-origin configuration","/blog/best-practices/cors","CORS Best Practices",[402,408],{"description":409,"href":410,"title":411},"Vercel header setup","/blog/best-practices/vercel","Vercel Best Practices",[402,413],{"description":414,"href":415,"title":416},"Netlify header setup","/blog/best-practices/netlify","Netlify Best Practices",[418,419,422,426],"cta-box",{"href":420,"label":421},"/","Start Free Scan",[26,423,425],{"id":424},"check-your-security-headers","Check Your Security Headers",[13,427,428],{},"Scan your site for missing or misconfigured security headers.",{"title":47,"searchDepth":430,"depth":430,"links":431},2,[432,433,442,447,448,449],{"id":28,"depth":430,"text":29},{"id":50,"depth":430,"text":51,"children":434},[435,437,438,439,440,441],{"id":55,"depth":436,"text":56},3,{"id":81,"depth":436,"text":82},{"id":186,"depth":436,"text":187},{"id":202,"depth":436,"text":203},{"id":218,"depth":436,"text":219},{"id":234,"depth":436,"text":235},{"id":250,"depth":430,"text":251,"children":443},[444,445,446],{"id":254,"depth":436,"text":255},{"id":267,"depth":436,"text":268},{"id":280,"depth":436,"text":281},{"id":293,"depth":430,"text":294},{"id":373,"depth":430,"text":374},{"id":424,"depth":430,"text":425},"best-practices","2026-01-27","Security headers best practices. Learn to configure Content Security Policy, HSTS, X-Frame-Options, and other security headers to protect your web application.",false,"md",[456,457,458],{"question":355,"answer":358},{"question":361,"answer":364},{"question":367,"answer":370},"vibe-green",null,{},true,"Protect your application with proper security header configuration.","/blog/best-practices/headers","14 min read","[object Object]","Article",{"title":5,"description":452},{"loc":464},"blog/best-practices/headers",[],"summary_large_image","2Jg8zPR2XKS9LptWa2Ehlqg_mp6u81LU2ZNUnpUs2Hg",1775843925793]