Is your Netlify site safe?
Netlify is a safe host: it provisions HTTPS certificates, serves your files from its CDN and keeps your build environment variables out of the browser unless your code puts them there. What it leaves to you is security headers, keeping secrets out of the front end build, and the code in your functions.
Updated
Check your live site now
Free, no signup, read only. A grade and plain fixes in seconds.
What Netlify secures, and what is your job
Netlify gives every site a certificate automatically and redirects http to https once the certificate is active. It builds your site from your repository and serves the output folder. Server code runs in Netlify Functions, which can read environment variables you set in the Netlify UI.
Your job is the part Netlify cannot know about. It does not add a Content-Security-Policy or frame protection unless you ask. It cannot tell a Stripe secret key from a public config value if your build tool puts both into the bundle. It cannot check that a function verifies the user before returning their data.
Security mistakes Netlify sites commonly make
- A secret in a VITE_, REACT_APP_ or NEXT_PUBLIC_ variable. The build tool inlines those into the JavaScript, and Netlify then serves that JavaScript to everyone.
- No _headers file, so the site has no CSP, no clickjacking protection and no Referrer-Policy.
- A _headers file in the project root of a Vite or Create React App project, where it never reaches the publish folder. It has to end up in dist or build.
- The publish directory set to the project root instead of the build output, which can put files like .env on the public site.
- A Netlify Function that returns Access-Control-Allow-Origin set to whatever origin asked, together with Access-Control-Allow-Credentials: true.
- Deploy previews and branch deploys that connect to the production database. Each one has its own public URL.
Where the fixes go on Netlify
Response headers go in a _headers file in your publish directory, or in [[headers]] blocks in netlify.toml. In a Vite project, put the file in public/ and Vite copies it into dist on every build. This is a starting point you can paste:
/*
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self' https: data: blob: 'unsafe-inline' 'unsafe-eval'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'; upgrade-insecure-requests
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()After adding a CSP, open the site with the browser console open and click through the main pages. Anything the policy blocks shows up as an error, and you add that source to the right directive.
Secrets go in Site configuration, Environment variables, without a public prefix, and get used only inside Netlify Functions. The browser calls /.netlify/functions/your-function, and the function calls the paid API with the key.
My site is on Netlify. Add a public/_headers file with Strict-Transport-Security, a Content-Security-Policy that fits the scripts and APIs this app really uses, X-Frame-Options DENY, X-Content-Type-Options nosniff and Referrer-Policy strict-origin-when-cross-origin. Then find any secret key in a VITE_ or other public variable, move the call that uses it into a Netlify Function, and read the key there from process.env without a public prefix.Netlify security checklist
- Confirm the publish directory is your build output (dist, build or out), not the repository root.
- Add a _headers file that ends up in the publish folder, and redeploy.
- Go through every environment variable with a public prefix. None should be a secret.
- Rotate any key that was ever public, then move its use into a function.
- In each function, check the caller's session before returning private data, and set CORS to your own origin.
- Decide whether deploy previews should reach production data, and protect them if they should not.
How to check your live Netlify site
curl -sI https://yoursite.netlify.app | grep -iE 'strict-transport|content-security|x-frame|x-content-type|referrer-policy'Each header you set should appear in the output. If none do, the _headers file did not make it into the publish folder.
An outside scan checks the same headers and grades the CSP (it flags unsafe-inline, unsafe-eval and wildcard script sources), then looks for secret keys in your HTML and JavaScript bundles, public .env, .git and config.json files, cookie flags and CORS on the page. It does not call your functions with test data or log in, so access checks inside your functions need a manual review.
Questions
Is Netlify safe to host my site on?
Yes. HTTPS, certificates and the serving infrastructure are handled for you. The settings that are yours are headers, which values reach the front end build, and the code in your functions.
Are Netlify environment variables secret?
They are private in the Netlify UI and in functions. They stop being private when your build tool inlines them, which Vite does for VITE_ variables and Create React App does for REACT_APP_ variables.
Why are my _headers not showing up on Netlify?
Usually the file is not in the publish directory. In a Vite project, put it in public/ so it is copied into dist. Check the deploy's file list to confirm it is there.
Does Netlify add a Content-Security-Policy by default?
No. A CSP depends on what your site loads, so you write it yourself, in _headers or netlify.toml.