[{"data":1,"prerenderedAt":498},["ShallowReactive",2],{"blog-best-practices/netlify":3},{"id":4,"title":5,"body":6,"category":473,"date":474,"dateModified":474,"description":475,"draft":476,"extension":477,"faq":478,"featured":476,"headerVariant":483,"image":484,"keywords":484,"meta":485,"navigation":486,"ogDescription":487,"ogTitle":484,"path":488,"readTime":489,"schemaOrg":490,"schemaType":491,"seo":492,"sitemap":493,"stem":494,"tags":495,"twitterCard":496,"__hash__":497},"blog/blog/best-practices/netlify.md","Netlify Security Best Practices: Headers, Functions, and Deployment",{"type":7,"value":8,"toc":454},"minimark",[9,20,29,34,37,52,57,66,70,73,128,137,146,150,153,162,166,169,178,182,185,189,202,206,215,219,222,231,235,238,243,263,267,339,368,396,400,403,423,442],[10,11,12],"tldr",{},[13,14,15,19],"p",{},[16,17,18],"strong",{},"The #1 Netlify security best practice is configuring proper security headers via _headers file or netlify.toml."," These 7 practices take about 40 minutes to implement and prevent 79% of common security issues in Netlify deployments. Focus on: adding security headers, protecting environment variables, securing Netlify Functions with authentication, and using deploy contexts for staging.",[21,22,23],"quotable-box",{},[24,25,26],"blockquote",{},[13,27,28],{},"\"Netlify makes deployment easy, but security is still your responsibility. Configure headers, protect your Functions, and never trust the client.\"",[30,31,33],"h2",{"id":32},"best-practice-1-configure-security-headers-5-min","Best Practice 1: Configure Security Headers 5 min",[13,35,36],{},"Add security headers using either a _headers file or netlify.toml:",[38,39,41],"code-block",{"label":40},"_headers file (place in publish directory)",[42,43,48],"pre",{"className":44,"code":46,"language":47},[45],"language-text","/*\n  X-Frame-Options: DENY\n  X-Content-Type-Options: nosniff\n  X-XSS-Protection: 1; mode=block\n  Referrer-Policy: strict-origin-when-cross-origin\n  Permissions-Policy: camera=(), microphone=(), geolocation=()\n\n# Stricter CSP for HTML pages\n/*.html\n  Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com\n\n# Cache static assets\n/assets/*\n  Cache-Control: public, max-age=31536000, immutable\n","text",[49,50,46],"code",{"__ignoreMap":51},"",[53,54,56],"h3",{"id":55},"alternative-netlifytoml-configuration","Alternative: netlify.toml Configuration",[38,58,60],{"label":59},"netlify.toml",[42,61,64],{"className":62,"code":63,"language":47},[45],"[[headers]]\n  for = \"/*\"\n  [headers.values]\n    X-Frame-Options = \"DENY\"\n    X-Content-Type-Options = \"nosniff\"\n    X-XSS-Protection = \"1; mode=block\"\n    Referrer-Policy = \"strict-origin-when-cross-origin\"\n\n[[headers]]\n  for = \"/*.html\"\n  [headers.values]\n    Content-Security-Policy = \"default-src 'self'; script-src 'self' 'unsafe-inline'\"\n",[49,65,63],{"__ignoreMap":51},[30,67,69],{"id":68},"best-practice-2-secure-environment-variables-5-min","Best Practice 2: Secure Environment Variables 5 min",[13,71,72],{},"Netlify environment variables are only available during build and in Functions:",[74,75,76,92],"table",{},[77,78,79],"thead",{},[80,81,82,86,89],"tr",{},[83,84,85],"th",{},"Context",[83,87,88],{},"Access to Env Vars",[83,90,91],{},"Security Implication",[93,94,95,107,117],"tbody",{},[80,96,97,101,104],{},[98,99,100],"td",{},"Build time",[98,102,103],{},"Yes",[98,105,106],{},"Variables can be baked into static files",[80,108,109,112,114],{},[98,110,111],{},"Netlify Functions",[98,113,103],{},[98,115,116],{},"Secure, server-side only",[80,118,119,122,125],{},[98,120,121],{},"Browser (static)",[98,123,124],{},"No (unless baked in)",[98,126,127],{},"Client-side code cannot read env vars",[129,130,131],"warning-box",{},[13,132,133,136],{},[16,134,135],{},"Important:"," Environment variables accessed during build can be included in your static bundle. Use Netlify Functions for operations requiring secrets instead of baking them into your frontend.",[38,138,140],{"label":139},"Safe pattern: Use Functions for secrets",[42,141,144],{"className":142,"code":143,"language":47},[45],"// netlify/functions/send-email.js\nexports.handler = async (event) => {\n  // Secret only available server-side\n  const apiKey = process.env.SENDGRID_API_KEY;\n\n  if (event.httpMethod !== 'POST') {\n    return { statusCode: 405, body: 'Method not allowed' };\n  }\n\n  const { to, subject, body } = JSON.parse(event.body);\n\n  // Send email using server-side secret\n  // ...\n\n  return { statusCode: 200, body: JSON.stringify({ success: true }) };\n};\n",[49,145,143],{"__ignoreMap":51},[30,147,149],{"id":148},"best-practice-3-secure-netlify-functions-10-min","Best Practice 3: Secure Netlify Functions 10 min",[13,151,152],{},"Netlify Functions are serverless endpoints that need proper security:",[38,154,156],{"label":155},"Secure Function pattern",[42,157,160],{"className":158,"code":159,"language":47},[45],"// netlify/functions/protected-action.js\nconst jwt = require('jsonwebtoken');\n\nexports.handler = async (event, context) => {\n  // Only allow POST requests\n  if (event.httpMethod !== 'POST') {\n    return {\n      statusCode: 405,\n      body: JSON.stringify({ error: 'Method not allowed' })\n    };\n  }\n\n  // Verify authentication\n  const authHeader = event.headers.authorization;\n  if (!authHeader || !authHeader.startsWith('Bearer ')) {\n    return {\n      statusCode: 401,\n      body: JSON.stringify({ error: 'Unauthorized' })\n    };\n  }\n\n  try {\n    const token = authHeader.substring(7);\n    const decoded = jwt.verify(token, process.env.JWT_SECRET);\n\n    // Validate input\n    const body = JSON.parse(event.body);\n    if (!body.action || typeof body.action !== 'string') {\n      return {\n        statusCode: 400,\n        body: JSON.stringify({ error: 'Invalid input' })\n      };\n    }\n\n    // Process authenticated request\n    const result = await processAction(decoded.userId, body.action);\n\n    return {\n      statusCode: 200,\n      body: JSON.stringify(result)\n    };\n  } catch (error) {\n    console.error('Function error:', error.message);\n    return {\n      statusCode: 401,\n      body: JSON.stringify({ error: 'Invalid token' })\n    };\n  }\n};\n",[49,161,159],{"__ignoreMap":51},[30,163,165],{"id":164},"best-practice-4-use-deploy-contexts-5-min","Best Practice 4: Use Deploy Contexts 5 min",[13,167,168],{},"Configure different settings for production, staging, and branch deploys:",[38,170,172],{"label":171},"netlify.toml with deploy contexts",[42,173,176],{"className":174,"code":175,"language":47},[45],"# Production settings\n[context.production]\n  environment = { NODE_ENV = \"production\" }\n\n[context.production.environment]\n  API_URL = \"https://api.yourdomain.com\"\n\n# Branch deploy settings (staging)\n[context.branch-deploy]\n  environment = { NODE_ENV = \"staging\" }\n\n[context.branch-deploy.environment]\n  API_URL = \"https://staging-api.yourdomain.com\"\n\n# Deploy preview settings\n[context.deploy-preview]\n  environment = { NODE_ENV = \"preview\" }\n\n# Set environment variables per context in Netlify Dashboard\n# for sensitive values like API keys\n",[49,177,175],{"__ignoreMap":51},[30,179,181],{"id":180},"best-practice-5-protect-deploy-previews-5-min","Best Practice 5: Protect Deploy Previews 5 min",[13,183,184],{},"Deploy previews can expose work in progress. Use these protections:",[53,186,188],{"id":187},"password-protection","Password Protection",[190,191,192,196,199],"ul",{},[193,194,195],"li",{},"Enable Site Protection in Site Settings > Access control",[193,197,198],{},"Set a password for branch deploys and deploy previews",[193,200,201],{},"Share password only with team members",[53,203,205],{"id":204},"identity-based-access","Identity-Based Access",[38,207,209],{"label":208},"_redirects for Netlify Identity protection",[42,210,213],{"className":211,"code":212,"language":47},[45],"# Protect staging site with Netlify Identity\n/*  200!  Role=admin,editor\n",[49,214,212],{"__ignoreMap":51},[30,216,218],{"id":217},"best-practice-6-configure-redirects-securely-5-min","Best Practice 6: Configure Redirects Securely 5 min",[13,220,221],{},"Use _redirects or netlify.toml for secure routing:",[38,223,225],{"label":224},"_redirects with security considerations",[42,226,229],{"className":227,"code":228,"language":47},[45],"# Force HTTPS\nhttp://yourdomain.com/* https://yourdomain.com/:splat 301!\nhttp://www.yourdomain.com/* https://yourdomain.com/:splat 301!\nhttps://www.yourdomain.com/* https://yourdomain.com/:splat 301!\n\n# Proxy API requests (hides backend URL)\n/api/*  https://your-backend.com/api/:splat  200\n\n# SPA fallback (but not for API routes)\n/*  /index.html  200\n",[49,230,228],{"__ignoreMap":51},[30,232,234],{"id":233},"best-practice-7-enable-security-features-5-min","Best Practice 7: Enable Security Features 5 min",[13,236,237],{},"Netlify provides built-in security features. Enable them:",[239,240,242],"h4",{"id":241},"netlify-security-features-checklist","Netlify Security Features Checklist:",[190,244,245,248,251,254,257,260],{},[193,246,247],{},"HTTPS enabled (automatic, but verify)",[193,249,250],{},"Asset optimization enabled (minification)",[193,252,253],{},"Deploy notifications configured",[193,255,256],{},"Build hooks secured (regenerate if exposed)",[193,258,259],{},"Forms spam protection enabled if using Netlify Forms",[193,261,262],{},"Audit log enabled (Team/Enterprise)",[30,264,266],{"id":265},"common-netlify-security-mistakes","Common Netlify Security Mistakes",[74,268,269,282],{},[77,270,271],{},[80,272,273,276,279],{},[83,274,275],{},"Mistake",[83,277,278],{},"Risk",[83,280,281],{},"Prevention",[93,283,284,295,306,317,328],{},[80,285,286,289,292],{},[98,287,288],{},"No security headers",[98,290,291],{},"XSS, clickjacking",[98,293,294],{},"Add _headers file with security headers",[80,296,297,300,303],{},[98,298,299],{},"Secrets in build output",[98,301,302],{},"Credential exposure",[98,304,305],{},"Use Functions for secret operations",[80,307,308,311,314],{},[98,309,310],{},"Unprotected Functions",[98,312,313],{},"Unauthorized access",[98,315,316],{},"Add authentication to all Functions",[80,318,319,322,325],{},[98,320,321],{},"Exposed build hooks",[98,323,324],{},"Unauthorized deploys",[98,326,327],{},"Keep hooks secret, regenerate if exposed",[80,329,330,333,336],{},[98,331,332],{},"Open deploy previews",[98,334,335],{},"Information disclosure",[98,337,338],{},"Password protect previews",[340,341,342],"info-box",{},[13,343,344,347,348,355,356,361,362,367],{},[16,345,346],{},"Official Resources:"," For the latest information, see ",[349,350,354],"a",{"href":351,"rel":352},"https://docs.netlify.com/configure-builds/file-based-configuration/",[353],"nofollow","Netlify Configuration Docs",", ",[349,357,360],{"href":358,"rel":359},"https://docs.netlify.com/routing/headers/",[353],"Netlify Headers Documentation",", and ",[349,363,366],{"href":364,"rel":365},"https://docs.netlify.com/functions/overview/",[353],"Netlify Functions Overview",".",[369,370,371,378,384,390],"faq-section",{},[372,373,375],"faq-item",{"question":374},"How do I add security headers on Netlify?",[13,376,377],{},"Create a _headers file in your publish directory with header rules, or add [[headers]] sections to netlify.toml. Headers apply to matched paths and are served by Netlify's CDN.",[372,379,381],{"question":380},"Are Netlify environment variables secure?",[13,382,383],{},"Yes, environment variables are encrypted and only available during build and in Functions. However, be careful not to expose them by logging or including them in client-side code during build.",[372,385,387],{"question":386},"Should I use _headers or netlify.toml?",[13,388,389],{},"Both work. _headers is simpler for just headers, while netlify.toml offers more configuration options in one place. Choose based on your preference and project complexity.",[372,391,393],{"question":392},"How do I protect Netlify Functions?",[13,394,395],{},"Implement authentication (JWT, API keys) in your Functions, validate all input, use proper HTTP methods, and avoid exposing sensitive information in error responses. Never trust client input.",[30,397,399],{"id":398},"further-reading","Further Reading",[13,401,402],{},"Put these practices into action with our step-by-step guides.",[190,404,405,411,417],{},[193,406,407],{},[349,408,410],{"href":409},"/blog/how-to/add-security-headers","Add security headers to your app",[193,412,413],{},[349,414,416],{"href":415},"/blog/checklists/pre-deployment-security-checklist","Pre-deployment security checklist",[193,418,419],{},[349,420,422],{"href":421},"/blog/getting-started/first-scan","Run your first security scan",[424,425,426,432,437],"related-articles",{},[427,428],"related-card",{"description":429,"href":430,"title":431},"Complete security guide","/blog/guides/netlify","Netlify Security Guide",[427,433],{"description":434,"href":435,"title":436},"Pre-launch checklist","/blog/checklists/netlify-security-checklist","Netlify Checklist",[427,438],{"description":439,"href":440,"title":441},"Security comparison","/blog/comparisons/vercel-vs-netlify","Vercel vs Netlify",[443,444,447,451],"cta-box",{"href":445,"label":446},"/","Start Free Scan",[30,448,450],{"id":449},"verify-your-netlify-security","Verify Your Netlify Security",[13,452,453],{},"Scan your Netlify deployment for security headers and configuration issues.",{"title":51,"searchDepth":455,"depth":455,"links":456},2,[457,461,462,463,464,468,469,470,471,472],{"id":32,"depth":455,"text":33,"children":458},[459],{"id":55,"depth":460,"text":56},3,{"id":68,"depth":455,"text":69},{"id":148,"depth":455,"text":149},{"id":164,"depth":455,"text":165},{"id":180,"depth":455,"text":181,"children":465},[466,467],{"id":187,"depth":460,"text":188},{"id":204,"depth":460,"text":205},{"id":217,"depth":455,"text":218},{"id":233,"depth":455,"text":234},{"id":265,"depth":455,"text":266},{"id":398,"depth":455,"text":399},{"id":449,"depth":455,"text":450},"best-practices","2026-01-29","Complete Netlify security best practices. Configure _headers files, secure Netlify Functions, and protect your deployment pipeline.",false,"md",[479,480,481,482],{"question":374,"answer":377},{"question":380,"answer":383},{"question":386,"answer":389},{"question":392,"answer":395},"netlify",null,{},true,"Secure your Netlify deployments with proper headers, function security, and configuration best practices.","/blog/best-practices/netlify","11 min read","[object Object]","Article",{"title":5,"description":475},{"loc":488},"blog/best-practices/netlify",[],"summary_large_image","eAfSaRDvh0Rwz_EtWX9GtsEXAVTpIJkA12RCJl_SSyY",1775843925706]