netlify-config
Configure Netlify projects via netlify.toml and the _headers/_redirects files — covering build settings and deploy contexts alongside environment variables/scopes and the Secrets Controller plus redirect/rewrite/proxy and custom-header rules. Use when setting a build command or publish directory, ad
By netlify · 2,391 installs
npx skills add netlify/context-and-tools --skill netlify-config
Source repository · Upstream listing
Netlify configuration
Config lives in three files at the repo root (or the base/package directory for monorepos):
netlify.toml — build, contexts, plugins, functions, redirects, headers, dev.
redirects — plain text redirect/rewrite rules, saved to the publish directory , no extension.
headers — plain text response headers, saved to the publish directory .
netlify.toml values take precedence over the Netlify UI when they conflict. Paths in netlify.toml are absolute relative to the base directory (root / by default).
Modern vs legacy syntax to reach for
Functions bundler: use node bundler = "esbuild" . zisi is the legacy JS default; TypeScript always uses esbuild .
Temporary redirect: use status = 302 . 307 is unsupported .
Gatsby Image CDN: use NETLIFY IMAGE CDN , not the deprecated GATSBY CLOUD IMAGE CDN .
Injecting env values into TOML: key = "$VAR" is NOT supported (except signed in proxy redirects). Use a build command sed substitution or a build plugin (see below).
netlify.toml build + contexts
[build] runs in Bash . Context aware keys include [build] and [[plugins]] — but NOT [[redirects]] or [[headers]] (those are always global). Precedence, least→most specific: UI < toml < any context property < [context.<name ] < [context.branchname] .
Redirects and rewrites
redirects rules are processed first , then netlify.toml ; within each, the first matching rule top to bottom wins — list specific rules before general ones. Edge functions run before redirects.
SPA history pushState fallback (required for clean URLs):
redirects syntax — from to [status] [conditions] , comments, paths case sensitive, URL encode special chars:
[[redirects]] keywords: from , to , status (default 301 ), force (default false ; ! /shadow), query ( query = {path = ":path"} ), conditions ( {Language, Country, Role, Cookie} ), headers (proxy request headers), signed (env var name for signed proxies).
Gotchas:
You cannot add/remove a trailing slash with a redirect — CDN normalizes URLs first; a /x/ → /x 301! rule loops infinitely. Rely on Pretty URLs (default on).
Splat asterisks work only at the end of a segment ( /jobs/ ), not mid path ( /jobs/ .html invalid). Placeholders ( :x ) only at the start of a segment; can't mix wildcard+placeholder in one segment.
You can't exclude a path from a splat; put a more specific rule first.
Country = ISO 3166 1 alpha 2; language redirects match only the first Accept Language entry.
Role based redirects with external auth providers are Enterprise only .
10,000+ redirects: use wildcards/placeholders or Edge Functions — oversized serialized output fails the deploy.
Proxies
Signed proxy ( signed names an env var scoped to Runtime ; must live in netlify.toml ; JWS is external only, not Netlify→Netlify):
Gotchas: cross team rewrites disallowed; same password site rewrites OK but not across separate protected sites; proxy timeout 26 s ; one hop by default; relative path assets break (use absolute or <base ); loops silently ignored.
Custom headers
Multi value — repeat the key ( headers ) or a multiline TOML string:
Gotchas:
Headers in headers / netlify.toml are global — NOT scoped to branch/context. Workaround: strip global headers, keep header files in a custom dir, and cp them into the publish dir from a per context build command:
Headers apply only to files from Netlify's store — NOT to proxied content or function/edge (SSR) responses; those must set their own headers.
Ignored (server set) names include Content Length , Content Encoding , Location (use redirects), Set Cookie , Server , etc.
Basic auth headers: Pro/Enterprise only . Cross subdomain cookies need a custom domain ( netlify.app is on the Public Suffix List).
Functions
Environment variables
Two storage methods:
UI / CLI / API — stored on Netlify (not the repo). Supports site + shared vars, per context values, scopes; reaches builds, functions/edge/ODB, snippet injection, forms, signed proxies. Recommended for anything sensitive.
netlify.toml — stored in the repo. Site vars only, per context values, no scope selection (everything gets Builds + Post processing ), reaches builds + snippet injection only.
netlify.toml env vars override same key UI/CLI/API vars.
Per context values in TOML:
CLI:
API: createEnvVars / updateEnvVar ( is secret: true ) / setEnvVarValue / deleteEnvVar / deleteEnvVarValue .
Access syntax: Bash $VAR in build.command / ignore.command ; process.env.VAR in Node scripts and plugins.
Scopes (Pro/Enterprise; default all): Builds (site builds) · Functions (Functions/Edge/ODB) · Runtime (forms, signed proxies) · Post processing (snippet injection). Shared vars are Pro/Enterprise and Team Owner only to read/edit. Precedence for a site+shared key collision resolves per scope — a site var only wins within the scopes it actually carries.
Naming/limits: keys alphanumeric + underscore, must start with a letter ( 1KEY , KEY1 invalid); keys ≤255 chars, values ≤5,000 chars. Read only variable names are reserved. Changes need a build + deploy.
Set the build language via reserved config vars — NODE VERSION , NPM FLAGS , YARN VERSION , BUN VERSION , RUBY VERSION , PHP VERSION , PYTHON VERSION , GO VERSION , HUGO VERSION , PNPM FLAGS , NPM TOKEN (Yarn: YARN NPM AUTH TOKEN ), etc.
Must be set in UI/CLI/API, NOT netlify.toml (read after the repo is cloned or a runtime only var): AWS LAMBDA JS RUNTIME , GIT LFS ENABLED , GIT LFS FETCH INCLUDE , NETLIFY BUILD DEBUG .
CI gotcha: defaults to true ; if it breaks a build, prepend CI='' to the build command.
Injecting env values into headers/redirects
key = "$VAR" is unsupported. Only path (scope must include Builds ):
sed substitution works only for [[headers]] / [[redirects]] (read after the build) and is not visible to build plugins (they run before the build command). For plugin visible changes, use a local build plugin editing netlifyConfig .
Useful read only build vars
CONTEXT ( production / deploy preview / branch deploy / dev ), BRANCH , COMMIT REF , CACHED COMMIT REF , PULL REQUEST , REVIEW ID , URL , DEPLOY URL , DEPLOY PRIME URL , SITE ID , SITE NAME .
Secrets Controller
Flag a var as secret: Contains secret values (UI) / secret (CLI) / is secret: true (API). Enforced, non customizable policy:
Secret values are write only — no readable version after set; the flag can't be removed to reveal it.
Secrets need explicit contexts + scopes; cannot carry the post processing scope.
Only code on Netlify (edge/serverless/build) reads unmasked values; off Netlify sees masked. The dev context value is exempt (unmasked from UI/CLI/API); netlify build never emits raw values.
Secret scanning runs automatically once any var is secret (and via smart detection). Fails the build on detection and logs the location. Configure via env vars set per context:
SECRETS SCAN ENABLED=false — disables all scanning (loses all secret protection).
SECRETS SCAN SMART DETECTION ENABLED=false — disables smart detection only.
SECRETS SCAN OMIT KEYS , SECRETS SCAN OMIT PATHS (comma lists; paths from repo root, globs OK).
SECRETS SCAN SMART DETECTION OMIT VALUES — safelist false positives ( prefer this over disabling). Smart detection is Personal/Pro/Enterprise.
Scanning covers all build files, values 4 chars and non boolean, searching plaintext + base64 + URI encoded permutations.
Sensitive variable policy (public repos only)
Governs whether untrusted deploys (unrecognized authors) get sensitive vars. Site members' Git deploys are always trusted, even from forks. Set at Project configuration Environment variables Site policies:
Require approval (default) — untrusted deploys wait for a member's approval.
Deploy without sensitive variables — builds run, sensitive vars withheld.
Deploy without restrictions — all vars present.
NOT available for GitHub Enterprise Server / GitLab self managed repos (treated as private).
Ignore builds
ignore under [build] decides whether to rebuild — runs from the base directory in Bash (or Node.js 18, fixed; site package.json deps not available). Exit 1 = changed → build continues; exit 0 = no change → build stops. A build hook always builds regardless of exit code.
Monorepos
Set the site subdirectory as the package directory (keep its netlify.toml there), leave base at root / , declare deps at the subdirectory level. Package directory is UI only — cannot be set in netlify.toml (Project configuration Developer settings Continuous deployment Build settings). Config file discovery order: package dir → base dir → root. Paths in netlify.toml stay absolute relative to the base directory. netlify <cmd filter <site selects a site.
JavaScript SPAs
Build command npm run <script / yarn <script ; publish dir often dist (framework dependent). Add the / /index.html 200 fallback (above) for pushState routing. Code splitting + hashed filenames with atomic deploys can throw Uncaught SyntaxError: Unexpected token on stale references — disable hashed filenames, use permalinks, or a service worker.
Netlify Dev [dev]
Does NOT run in Bash (no Bash syntax in command ). There is no environment key — set local env vars under [context.dev.environment] .
Plugins & extensions
Full reference pages: build environment variables at https://docs.netlify.com/build/configure builds/environment variables.md, env var overview at https://docs.netlify.com/build/environment variables/overview.md, Secrets Controller at https://docs.netlify.com/build/environment variables/secrets controller.md, redirects at https://docs.netlify.com/manage/routing/redirects/overview.md, redirect options at https://docs.netlify.com/manage/routing/redirects/redirect options.md, rewrites/proxies at https://docs.netlify.com/manage/routing/redirects/rewrites proxies.md, custom headers at https://docs.netlify.com/manage/routing/headers.md, and file based config at https://docs.netlify.com/build/configure builds/file based configuration.md.
<! Plan gating for the sensitive variable policy itself is unspecified in the sources; only its public repo requirement and the smart detection plan list are documented.
<! system: agent context/config/system.md — human owned, merged by ctx gen; edit system.md, not this section
Netlify house rules (config)
These are org conventions, not docs facts — merged into the rendered skill by
ctx gen and never generated. Owned by the skills maintainer.
1. Env vars set in netlify.toml are NOT available to functions or edge
functions at runtime — reading them there returns undefined . Set
runtime vars in the UI or with netlify env:set , not netlify.toml .
2. Never put secrets in client prefixed env vars ( VITE , NEXT PUBLIC ,
PUBLIC , ...) — they are inlined into the client bundle; secret
does not protect them.
3. When snapshotting env vars locally ( netlify env:list plain .env ),
keep .env gitignored — never commit it.
4. State env var scope interaction explicitly: a site variable scoped to
Builds does not shadow the shared variable for other scopes — precedence
resolves independently per scope (site beats shared only within the
scopes the site variable actually carries).