TL;DR
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.
What Netlify Handles Automatically
Netlify's infrastructure provides several security benefits out of the box:
- Automatic SSL: All sites get free SSL certificates from Let's Encrypt
- CDN: Content is served from a global CDN with built-in protection
- DDoS mitigation: Enterprise-grade DDoS protection included
- Atomic deploys: Deploys are atomic, preventing partial deployments
- Immutable deploys: Each deploy is immutable and can be rolled back
Environment Variables on Netlify
Unlike Vercel's NEXT_PUBLIC_ convention, Netlify environment variables work differently depending on your framework:
| Framework | Public Prefix | Example |
|---|---|---|
| React (Create React App) | REACT_APP_ | REACT_APP_API_URL |
| Next.js | NEXT_PUBLIC_ | NEXT_PUBLIC_SITE_URL |
| Gatsby | GATSBY_ | GATSBY_API_KEY |
| Vue/Vite | VITE_ | VITE_APP_URL |
Key rule: Variables with framework-specific public prefixes are bundled into your JavaScript and visible in the browser. Never put secrets in these variables.
Setting Environment Variables
You can set environment variables in multiple places:
- Netlify Dashboard: Site settings > Build & deploy > Environment
- netlify.toml: For non-sensitive values only
- Netlify CLI: For local development
[build]
publish = "dist"
command = "npm run build"
[build.environment]
NODE_VERSION = "18"
# OK: non-sensitive configuration
SITE_URL = "https://mysite.netlify.app"
# NEVER put secrets here - this file is in your repo!
Netlify Functions Security
Netlify Functions are serverless functions that run on AWS Lambda. They can access your environment variables securely:
// netlify/functions/api.js
exports.handler = async (event, context) => {
// Check for authentication
const authHeader = event.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return {
statusCode: 401,
body: JSON.stringify({ error: 'Unauthorized' }),
};
}
const token = authHeader.substring(7);
// Verify the token (example with JWT)
try {
const user = verifyToken(token);
// Access secrets safely (server-side only)
const apiKey = process.env.SECRET_API_KEY;
// Process request...
return {
statusCode: 200,
body: JSON.stringify({ data: 'Success' }),
};
} catch (error) {
return {
statusCode: 401,
body: JSON.stringify({ error: 'Invalid token' }),
};
}
};
Input Validation in Functions
exports.handler = async (event) => {
// Only allow POST
if (event.httpMethod !== 'POST') {
return { statusCode: 405, body: 'Method Not Allowed' };
}
// Parse and validate body
let data;
try {
data = JSON.parse(event.body);
} catch {
return { statusCode: 400, body: 'Invalid JSON' };
}
// Validate required fields
if (!data.email || !isValidEmail(data.email)) {
return { statusCode: 400, body: 'Valid email required' };
}
if (!data.message || data.message.length > 1000) {
return { statusCode: 400, body: 'Message required (max 1000 chars)' };
}
// Process valid data...
};
Netlify Forms Security
Netlify Forms is convenient but needs spam protection:
<form name="contact" method="POST" data-netlify="true" netlify-honeypot="bot-field">
<!-- Hidden honeypot field - bots fill this, humans don't -->
<p class="hidden">
<label>Don't fill this out: <input name="bot-field" /></label>
</p>
<p>
<label>Email: <input type="email" name="email" required /></label>
</p>
<p>
<label>Message: <textarea name="message" required></textarea></label>
</p>
<p>
<button type="submit">Send</button>
</p>
</form>
<style>
.hidden { display: none; }
</style>
reCAPTCHA Integration
<form name="contact" method="POST" data-netlify="true" data-netlify-recaptcha="true">
<p>
<label>Email: <input type="email" name="email" required /></label>
</p>
<p>
<label>Message: <textarea name="message" required></textarea></label>
</p>
<div data-netlify-recaptcha="true"></div>
<p>
<button type="submit">Send</button>
</p>
</form>
Security Headers Configuration
Configure security headers in netlify.toml:
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
X-XSS-Protection = "1; mode=block"
Referrer-Policy = "strict-origin-when-cross-origin"
Permissions-Policy = "camera=(), microphone=(), geolocation=()"
[[headers]]
for = "/*.js"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
[[headers]]
for = "/api/*"
[headers.values]
Access-Control-Allow-Origin = "https://yourdomain.com"
Access-Control-Allow-Methods = "GET, POST, OPTIONS"
Deploy Previews Security
Netlify creates deploy previews for pull requests. Security considerations:
- Deploy previews are public by default
- They share environment variables with production (unless configured otherwise)
- URLs follow a predictable pattern
Deploy Preview Protection Options
Use different environment variables for deploy previews
Enable password protection (Team/Business plans)
Disable deploy previews for sensitive repos
Don't connect production databases to previews
Netlify Identity Security
If using Netlify Identity for authentication:
// Check identity in a function
const { user } = context.clientContext;
if (!user) {
return {
statusCode: 401,
body: JSON.stringify({ error: 'Please log in' }),
};
}
// Check user roles
if (!user.app_metadata.roles?.includes('admin')) {
return {
statusCode: 403,
body: JSON.stringify({ error: 'Admin access required' }),
};
}
Netlify Security Checklist
Before Going Live
No secrets in netlify.toml
No secrets in public environment variables
Functions validate all input
Functions check authentication
Forms have spam protection
Security headers configured
Deploy previews are protected or use test data
Redirect rules don't expose sensitive paths
Are environment variables in netlify.toml secure?
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.
Can people access my Netlify Functions code?
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.
How do I prevent spam on Netlify Forms?
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.
Are deploy preview URLs discoverable?
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.