IS NETLIFY SAFE? BUILD LOGS, REDIRECT RULES & FUNCTIONS AUDIT
JAMstack Security Model
Netlify’s JAMstack approach (JavaScript, APIs, Markup) reduces attack surface by pre-building static assets. Most server-side vulnerabilities — SSRF against an app server, SQL injection via a runtime form handler, RCE via a vulnerable backend — simply don’t apply because there is no long-running app server. The catch is that the moment you reach for Functions, Forms, Identity, or Edge Functions, you’re back in the same threat model as any other serverless platform, just with Netlify-specific defaults.
The four leak categories we keep finding on production Netlify sites: env vars printed in build logs, open redirect rules in _redirects, Functions deployed without auth, and Edge Functions that mutate headers in ways that bypass downstream checks. None of these are platform bugs. All four are configuration choices that look fine on a first read.
Security Considerations
Netlify Functions
Netlify Functions are AWS Lambda under the hood. The function itself runs in an isolated environment, but the handler is whatever you wrote. Common failures we see:
- The function reads
event.headers['x-forwarded-for']and trusts it for rate limiting. Spoof the header, bypass the rate limit. Usecontext.ipfrom the new Functions API. - The function exposes a “test” endpoint (
/.netlify/functions/debug) that dumps env vars or runs arbitrary SQL. The “we’ll remove it later” endpoint that ends up indexed by Google. - The function returns the result of
JSON.stringify(process.env)in an error path because the developer wanted to “see what’s wrong in prod”. Now everyone can see what’s wrong in prod.
// netlify/functions/get-user.js — wrong
exports.handler = async (event) => {
const id = event.queryStringParameters.id;
const user = await db.query(`SELECT * FROM users WHERE id = ${id}`);
return { statusCode: 200, body: JSON.stringify(user) };
};
// right
exports.handler = async (event, context) => {
const session = await getSession(event);
if (!session) return { statusCode: 401, body: '' };
const id = event.queryStringParameters.id;
if (!/^[0-9a-f-]{36}$/.test(id)) return { statusCode: 400, body: '' };
const user = await db.query('SELECT id, email FROM users WHERE id = $1', [id]);
return { statusCode: 200, body: JSON.stringify(user) };
};
Form Submissions
Netlify Forms are convenient and the spam protection is opt-in. Without honeypot or reCAPTCHA, a public form will start receiving spam within hours of going live, and Netlify will rate-limit your submission inbox. Worse, if you forward submissions to email or Slack, you’re now forwarding attacker payloads into a UI that might render them.
<form name="contact" method="POST" data-netlify="true" data-netlify-honeypot="bot-field">
<input type="hidden" name="form-name" value="contact" />
<p hidden><label>Don't fill this out: <input name="bot-field" /></label></p>
<input type="email" name="email" required />
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>
Treat anything submitted via Forms as untrusted input even when it lands in your Slack — sanitize before rendering downstream.
Environment Variables
Netlify env vars can be scoped per context (production, deploy-preview, branch-deploy) and per branch. The default UI nudges you to set everything for “All scopes,” which means your Stripe live key is available in every Pull Request preview. Anyone with the deploy URL can hit the function and use the production key.
# Scope a secret to production only
netlify env:set STRIPE_SECRET_KEY sk_live_xxx --context production
netlify env:list
Build-time variables prefixed with frameworks’ public prefixes (NEXT_PUBLIC_, VITE_, PUBLIC_, GATSBY_) are bundled into the client. Treat them as public.
Build Logs
Build logs are visible to every team member and survive after deploys. A printenv or env line in your build script will dump every secret in plaintext to that log, where it stays. Audit your build commands and any custom plugins for printenv, env, set, and console.log(process.env.
Deploy Previews
Deploy previews are public by default. The URL pattern is guessable per PR, and Netlify’s GitHub integration posts the link as a PR comment — so anyone watching the public repo gets it for free. Use Password Protection (Pro+) or Role-based access control (Enterprise) for projects with sensitive features.
Redirect Rules
_redirects and netlify.toml redirects are the cleanest way to ship an open redirect. The pattern that ships in production:
# _redirects — open redirect, do not use
/go /:url 302
A user visits /go?url=https://attacker.example/login and lands on the phishing page wearing your domain in the referrer. Always allowlist destinations:
/go/docs https://docs.example.com 302
/go/blog https://blog.example.com 302
Same trap exists with proxy rules — /api/* https://:host/:splat 200 is a fully open SSRF if host is user-controlled.
Common Mistakes We See in Audits
- Production secrets duplicated to “All scopes,” exposing them in deploy previews.
- Functions endpoints without any auth check, discovered via
/.netlify/functions/URL fuzzing. _redirectsproxy rules with placeholder hosts that effectively act as open proxies.- Build logs containing
npminstall output that printed env vars due to a misbehaving postinstall script. - Edge Functions that strip
Authorizationheaders when forwarding to origin, defeating the origin’s auth.
Comparison vs Vercel
Both platforms have similar SOC 2 posture and similar default exposure. The differences:
- Netlify’s
_redirectsis plain text and easy to misread. Vercel’svercel.jsonis structured but easier to over-permission. - Netlify Forms is built-in and unique to the platform. Spam protection is opt-in.
- Vercel’s Deployment Protection is more polished. Netlify’s Site Password is older and clunkier.
- Both ship
NEXT_PUBLIC_*/VITE_*env vars to the browser. Both leak secrets the same way when developers forget.
Enterprise Considerations
- SSO: SAML SSO is Enterprise-only. Below that, every team member has a personal account.
- Audit Logs: Team Audit Log is on Pro+; full deployment and CLI activity is Enterprise.
- SOC 2 boundary: Netlify covers the platform. Your application’s handling of customer data is yours.
- Secret rotation: No built-in rotation. Use Doppler, Infisical, or rotate via the Netlify API on a schedule.
Security Assessment
Strengths
-
- Enterprise-grade CDN and infrastructure
-
- Automatic HTTPS with Let’s Encrypt and managed certs
-
- SOC 2 Type II compliance
-
- Built-in DDoS protection
-
- Encrypted environment variables with per-context scoping
-
- Deploy previews with optional access controls
-
- Forms with built-in (opt-in) spam protection
Concerns
-
- Netlify Functions security is developer responsibility
-
- Default “All scopes” env var setting leaks production secrets to previews
-
_redirectsand proxy rules trivially become open redirects / SSRF
-
- Build logs can leak secrets and are visible team-wide
-
- Form submissions need explicit spam protection
-
- Deploy previews are public by default
The Verdict
Netlify is a safe deployment platform with excellent infrastructure security. The JAMstack model meaningfully reduces server-side attack surface compared to traditional hosting. The risks live in the four places where your config meets Netlify’s defaults: Functions auth, env var scoping, redirect rules, and Preview exposure. Get those right and the platform takes care of the rest.
Related Resources
How to Secure Netlify
Step-by-step guide covering env var scoping, Functions auth patterns, redirect rule auditing, and Preview protection.
Netlify Security Checklist
Interactive checklist for the four launch-blockers above plus the quarterly review items.
Is Vercel Safe?
Side-by-side comparison of the most common deployment failure modes on the two leading JAMstack platforms.
Scan Your Netlify Site
Let VibeEval scan your Netlify deployment for security vulnerabilities — including the open-redirect, Functions-without-auth, and exposed-env-var patterns that account for most incidents.
SCAN YOUR APP
14-day trial. No card. Results in under 60 seconds.