[{"data":1,"prerenderedAt":445},["ShallowReactive",2],{"blog-guides/netlify":3},{"id":4,"title":5,"body":6,"category":425,"date":426,"dateModified":426,"description":427,"draft":428,"extension":429,"faq":430,"featured":428,"headerVariant":431,"image":430,"keywords":430,"meta":432,"navigation":433,"ogDescription":434,"ogTitle":430,"path":435,"readTime":436,"schemaOrg":437,"schemaType":438,"seo":439,"sitemap":440,"stem":441,"tags":442,"twitterCard":443,"__hash__":444},"blog/blog/guides/netlify.md","Netlify Security Guide: Functions, Environment Variables, and Forms",{"type":7,"value":8,"toc":406},"minimark",[9,16,21,24,59,63,66,133,142,147,150,171,186,190,193,202,206,215,219,222,231,235,244,248,255,264,268,271,282,287,290,293,296,299,303,306,315,319,323,326,329,332,335,338,341,344,347,375,394],[10,11,12],"tldr",{},[13,14,15],"p",{},"Netlify provides automatic SSL, DDoS protection, and secure infrastructure. Your focus should be on environment variables (keep secrets server-side), securing Netlify Functions with authentication, protecting forms from spam, and configuring security headers in netlify.toml. Deploy previews are public by default, so be careful with sensitive features.",[17,18,20],"h2",{"id":19},"what-netlify-handles-automatically","What Netlify Handles Automatically",[13,22,23],{},"Netlify's infrastructure provides several security benefits out of the box:",[25,26,27,35,41,47,53],"ul",{},[28,29,30,34],"li",{},[31,32,33],"strong",{},"Automatic SSL:"," All sites get free SSL certificates from Let's Encrypt",[28,36,37,40],{},[31,38,39],{},"CDN:"," Content is served from a global CDN with built-in protection",[28,42,43,46],{},[31,44,45],{},"DDoS mitigation:"," Enterprise-grade DDoS protection included",[28,48,49,52],{},[31,50,51],{},"Atomic deploys:"," Deploys are atomic, preventing partial deployments",[28,54,55,58],{},[31,56,57],{},"Immutable deploys:"," Each deploy is immutable and can be rolled back",[17,60,62],{"id":61},"environment-variables-on-netlify","Environment Variables on Netlify",[13,64,65],{},"Unlike Vercel's NEXT_PUBLIC_ convention, Netlify environment variables work differently depending on your framework:",[67,68,69,85],"table",{},[70,71,72],"thead",{},[73,74,75,79,82],"tr",{},[76,77,78],"th",{},"Framework",[76,80,81],{},"Public Prefix",[76,83,84],{},"Example",[86,87,88,100,111,122],"tbody",{},[73,89,90,94,97],{},[91,92,93],"td",{},"React (Create React App)",[91,95,96],{},"REACT_APP_",[91,98,99],{},"REACT_APP_API_URL",[73,101,102,105,108],{},[91,103,104],{},"Next.js",[91,106,107],{},"NEXT_PUBLIC_",[91,109,110],{},"NEXT_PUBLIC_SITE_URL",[73,112,113,116,119],{},[91,114,115],{},"Gatsby",[91,117,118],{},"GATSBY_",[91,120,121],{},"GATSBY_API_KEY",[73,123,124,127,130],{},[91,125,126],{},"Vue/Vite",[91,128,129],{},"VITE_",[91,131,132],{},"VITE_APP_URL",[134,135,136],"warning-box",{},[13,137,138,141],{},[31,139,140],{},"Key rule:"," Variables with framework-specific public prefixes are bundled into your JavaScript and visible in the browser. Never put secrets in these variables.",[143,144,146],"h3",{"id":145},"setting-environment-variables","Setting Environment Variables",[13,148,149],{},"You can set environment variables in multiple places:",[151,152,153,159,165],"ol",{},[28,154,155,158],{},[31,156,157],{},"Netlify Dashboard:"," Site settings > Build & deploy > Environment",[28,160,161,164],{},[31,162,163],{},"netlify.toml:"," For non-sensitive values only",[28,166,167,170],{},[31,168,169],{},"Netlify CLI:"," For local development",[172,173,175],"code-block",{"label":174},"netlify.toml - non-sensitive only",[176,177,182],"pre",{"className":178,"code":180,"language":181},[179],"language-text","[build]\n  publish = \"dist\"\n  command = \"npm run build\"\n\n[build.environment]\n  NODE_VERSION = \"18\"\n  # OK: non-sensitive configuration\n  SITE_URL = \"https://mysite.netlify.app\"\n\n  # NEVER put secrets here - this file is in your repo!\n","text",[183,184,180],"code",{"__ignoreMap":185},"",[17,187,189],{"id":188},"netlify-functions-security","Netlify Functions Security",[13,191,192],{},"Netlify Functions are serverless functions that run on AWS Lambda. They can access your environment variables securely:",[172,194,196],{"label":195},"Secure function with authentication",[176,197,200],{"className":198,"code":199,"language":181},[179],"// netlify/functions/api.js\nexports.handler = async (event, context) => {\n  // Check for authentication\n  const authHeader = event.headers.authorization;\n\n  if (!authHeader || !authHeader.startsWith('Bearer ')) {\n    return {\n      statusCode: 401,\n      body: JSON.stringify({ error: 'Unauthorized' }),\n    };\n  }\n\n  const token = authHeader.substring(7);\n\n  // Verify the token (example with JWT)\n  try {\n    const user = verifyToken(token);\n\n    // Access secrets safely (server-side only)\n    const apiKey = process.env.SECRET_API_KEY;\n\n    // Process request...\n    return {\n      statusCode: 200,\n      body: JSON.stringify({ data: 'Success' }),\n    };\n  } catch (error) {\n    return {\n      statusCode: 401,\n      body: JSON.stringify({ error: 'Invalid token' }),\n    };\n  }\n};\n",[183,201,199],{"__ignoreMap":185},[143,203,205],{"id":204},"input-validation-in-functions","Input Validation in Functions",[172,207,209],{"label":208},"Validate input data",[176,210,213],{"className":211,"code":212,"language":181},[179],"exports.handler = async (event) => {\n  // Only allow POST\n  if (event.httpMethod !== 'POST') {\n    return { statusCode: 405, body: 'Method Not Allowed' };\n  }\n\n  // Parse and validate body\n  let data;\n  try {\n    data = JSON.parse(event.body);\n  } catch {\n    return { statusCode: 400, body: 'Invalid JSON' };\n  }\n\n  // Validate required fields\n  if (!data.email || !isValidEmail(data.email)) {\n    return { statusCode: 400, body: 'Valid email required' };\n  }\n\n  if (!data.message || data.message.length > 1000) {\n    return { statusCode: 400, body: 'Message required (max 1000 chars)' };\n  }\n\n  // Process valid data...\n};\n",[183,214,212],{"__ignoreMap":185},[17,216,218],{"id":217},"netlify-forms-security","Netlify Forms Security",[13,220,221],{},"Netlify Forms is convenient but needs spam protection:",[172,223,225],{"label":224},"Form with honeypot spam protection",[176,226,229],{"className":227,"code":228,"language":181},[179],"\u003Cform name=\"contact\" method=\"POST\" data-netlify=\"true\" netlify-honeypot=\"bot-field\">\n  \u003C!-- Hidden honeypot field - bots fill this, humans don't -->\n  \u003Cp class=\"hidden\">\n    \u003Clabel>Don't fill this out: \u003Cinput name=\"bot-field\" />\u003C/label>\n  \u003C/p>\n\n  \u003Cp>\n    \u003Clabel>Email: \u003Cinput type=\"email\" name=\"email\" required />\u003C/label>\n  \u003C/p>\n  \u003Cp>\n    \u003Clabel>Message: \u003Ctextarea name=\"message\" required>\u003C/textarea>\u003C/label>\n  \u003C/p>\n  \u003Cp>\n    \u003Cbutton type=\"submit\">Send\u003C/button>\n  \u003C/p>\n\u003C/form>\n\n\u003Cstyle>\n  .hidden { display: none; }\n\u003C/style>\n",[183,230,228],{"__ignoreMap":185},[143,232,234],{"id":233},"recaptcha-integration","reCAPTCHA Integration",[172,236,238],{"label":237},"Form with reCAPTCHA",[176,239,242],{"className":240,"code":241,"language":181},[179],"\u003Cform name=\"contact\" method=\"POST\" data-netlify=\"true\" data-netlify-recaptcha=\"true\">\n  \u003Cp>\n    \u003Clabel>Email: \u003Cinput type=\"email\" name=\"email\" required />\u003C/label>\n  \u003C/p>\n  \u003Cp>\n    \u003Clabel>Message: \u003Ctextarea name=\"message\" required>\u003C/textarea>\u003C/label>\n  \u003C/p>\n  \u003Cdiv data-netlify-recaptcha=\"true\">\u003C/div>\n  \u003Cp>\n    \u003Cbutton type=\"submit\">Send\u003C/button>\n  \u003C/p>\n\u003C/form>\n",[183,243,241],{"__ignoreMap":185},[17,245,247],{"id":246},"security-headers-configuration","Security Headers Configuration",[13,249,250,251,254],{},"Configure security headers in ",[183,252,253],{},"netlify.toml",":",[172,256,258],{"label":257},"netlify.toml security headers",[176,259,262],{"className":260,"code":261,"language":181},[179],"[[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    Permissions-Policy = \"camera=(), microphone=(), geolocation=()\"\n\n[[headers]]\n  for = \"/*.js\"\n  [headers.values]\n    Cache-Control = \"public, max-age=31536000, immutable\"\n\n[[headers]]\n  for = \"/api/*\"\n  [headers.values]\n    Access-Control-Allow-Origin = \"https://yourdomain.com\"\n    Access-Control-Allow-Methods = \"GET, POST, OPTIONS\"\n",[183,263,261],{"__ignoreMap":185},[17,265,267],{"id":266},"deploy-previews-security","Deploy Previews Security",[13,269,270],{},"Netlify creates deploy previews for pull requests. Security considerations:",[25,272,273,276,279],{},[28,274,275],{},"Deploy previews are public by default",[28,277,278],{},"They share environment variables with production (unless configured otherwise)",[28,280,281],{},"URLs follow a predictable pattern",[283,284,286],"h4",{"id":285},"deploy-preview-protection-options","Deploy Preview Protection Options",[13,288,289],{},"Use different environment variables for deploy previews",[13,291,292],{},"Enable password protection (Team/Business plans)",[13,294,295],{},"Disable deploy previews for sensitive repos",[13,297,298],{},"Don't connect production databases to previews",[17,300,302],{"id":301},"netlify-identity-security","Netlify Identity Security",[13,304,305],{},"If using Netlify Identity for authentication:",[172,307,309],{"label":308},"Protect routes with Netlify Identity",[176,310,313],{"className":311,"code":312,"language":181},[179],"// Check identity in a function\nconst { user } = context.clientContext;\n\nif (!user) {\n  return {\n    statusCode: 401,\n    body: JSON.stringify({ error: 'Please log in' }),\n  };\n}\n\n// Check user roles\nif (!user.app_metadata.roles?.includes('admin')) {\n  return {\n    statusCode: 403,\n    body: JSON.stringify({ error: 'Admin access required' }),\n  };\n}\n",[183,314,312],{"__ignoreMap":185},[17,316,318],{"id":317},"netlify-security-checklist","Netlify Security Checklist",[283,320,322],{"id":321},"before-going-live","Before Going Live",[13,324,325],{},"No secrets in netlify.toml",[13,327,328],{},"No secrets in public environment variables",[13,330,331],{},"Functions validate all input",[13,333,334],{},"Functions check authentication",[13,336,337],{},"Forms have spam protection",[13,339,340],{},"Security headers configured",[13,342,343],{},"Deploy previews are protected or use test data",[13,345,346],{},"Redirect rules don't expose sensitive paths",[348,349,350,357,363,369],"faq-section",{},[351,352,354],"faq-item",{"question":353},"Are environment variables in netlify.toml secure?",[13,355,356],{},"No. netlify.toml is typically committed to your repository and is public. Only put non-sensitive configuration there. Set secrets through the Netlify Dashboard or CLI.",[351,358,360],{"question":359},"Can people access my Netlify Functions code?",[13,361,362],{},"No, the function source code is not publicly accessible. Users can only interact with functions through HTTP requests. Your code runs securely on Netlify's infrastructure.",[351,364,366],{"question":365},"How do I prevent spam on Netlify Forms?",[13,367,368],{},"Use a combination of honeypot fields and reCAPTCHA. Netlify also has built-in spam filtering. For high-value forms, consider using a custom function with additional validation.",[351,370,372],{"question":371},"Are deploy preview URLs discoverable?",[13,373,374],{},"Deploy preview URLs follow a pattern (deploy-preview-{number}--{site-name}.netlify.app) so they can be guessed. For sensitive sites, use password protection or disable deploy previews.",[376,377,378,384,389],"related-articles",{},[379,380],"related-card",{"description":381,"href":382,"title":383},"Compare deployment platforms","/blog/comparisons/vercel-vs-netlify","Vercel vs Netlify Security",[379,385],{"description":386,"href":387,"title":388},"Platform security analysis","/blog/is-safe/netlify","Is Netlify Safe?",[379,390],{"description":391,"href":392,"title":393},"Step-by-step guide","/blog/how-to/add-security-headers","Adding Security Headers",[395,396,399,403],"cta-box",{"href":397,"label":398},"/","Start Free Scan",[17,400,402],{"id":401},"deploying-to-netlify","Deploying to Netlify?",[13,404,405],{},"Scan your project for security issues before going live.",{"title":185,"searchDepth":407,"depth":407,"links":408},2,[409,410,414,417,420,421,422,423,424],{"id":19,"depth":407,"text":20},{"id":61,"depth":407,"text":62,"children":411},[412],{"id":145,"depth":413,"text":146},3,{"id":188,"depth":407,"text":189,"children":415},[416],{"id":204,"depth":413,"text":205},{"id":217,"depth":407,"text":218,"children":418},[419],{"id":233,"depth":413,"text":234},{"id":246,"depth":407,"text":247},{"id":266,"depth":407,"text":267},{"id":301,"depth":407,"text":302},{"id":317,"depth":407,"text":318},{"id":401,"depth":407,"text":402},"guides","2026-01-22","Complete security guide for Netlify. Learn to protect environment variables, secure serverless functions, handle forms safely, and configure security headers.",false,"md",null,"blue",{},true,"Secure your Netlify deployments with proper configuration and best practices.","/blog/guides/netlify","9 min read","[object Object]","Article",{"title":5,"description":427},{"loc":435},"blog/guides/netlify",[],"summary_large_image","fyJzG7YUxfIbq64XXG0PhjUCbtqvsA8yuyKggEJDcLo",1775843930121]