A Netlify personal access token acts as the user who created it, so one token pasted into a CI variable can deploy, reconfigure or delete every site that user can reach. Netlify has no team-owned API key: automation runs on personal access tokens, the CLI's stored login, OAuth application credentials, build hook URLs and deploy keys, and each behaves differently when it leaks or when its owner leaves. Netlify also holds the secrets your builds and functions use, which makes its environment variable controls part of the same problem. This guide maps every Netlify credential type, the roles that can create or expose them, how to harden and monitor them, and what to do when one leaks. It is written for platform, web and secrets management teams. The Heroku, Netlify and Render PaaS Security Guide gives the platform overview; this page goes deeper on Netlify credentials.
Key takeaways
- Every API token is a person. Personal access tokens (PATs) and CLI logins carry their creator's rights on every team they belong to, so create automation tokens from a dedicated, minimally privileged member.
- Always set an expiry. The token form asks for an expiration date; pick the shortest that fits the job and record it, because nothing else reminds you.
- SSO teams block tokens by default. A PAT reaches a SAML SSO team only if Allow access to my SAML-based Netlify team was ticked while signed in through SSO, which is a useful choke point.
- Build hook URLs are bearer credentials. Anyone with the URL can POST to it and start a build; treat it like a secret and delete hooks you do not use.
- A password reset is the bulk kill switch. Resetting the Netlify password permanently invalidates all PATs and OAuth tokens created before the reset.
How identity and access work in Netlify
People belong to a team (and, on Enterprise, an organisation); sites, now called projects in the UI, belong to teams. There is no separate machine principal, so every non-human credential is issued by a human account or by Netlify itself on behalf of a site.
| Credential | Created where | Acts as | Lifetime | Main risk |
|---|---|---|---|---|
| Personal access token | User settings, Applications, Personal access tokens, New access token | The creating user, on every team they belong to (SSO teams only if allowed) | Until the chosen expiry, deletion or a password reset | Stored in CI as NETLIFY_AUTH_TOKEN, copied between pipelines |
| CLI login token | netlify login browser flow | The user, through the Netlify CLI authorised application | Until revoked under Authorized applications or a password reset | Plaintext in the CLI config.json on laptops |
| OAuth application client key and secret | User settings, OAuth applications | Your integration, requesting user authorisation at app.netlify.com/authorize | Until the application is changed or deleted | Client secret leaking from an integration's code or config |
| OAuth access tokens issued to apps | Users authorising an integration | The authorising user | Until revoked or a password reset | Third-party integration compromise |
| Build hook URL | Project configuration, Developer settings, Continuous deployment, Build hooks | Anyone who has the URL | Until the hook is deleted or the repository unlinked | Unauthenticated build triggering, cache clearing, branch selection |
| Deploy key | Continuous deployment, Deploy key, Generate public key | Netlify's build, reading a private submodule | Until removed from the Git provider | Read access to source; on Azure DevOps the key is tied to a user |
| Git provider tokens | Netlify GitHub App, or OAuth tokens for GitLab, Bitbucket and some older GitHub sites | Netlify, against your repositories | GitHub App tokens expire after one hour | Over-broad repository selection; netlify init stores a GitHub account-level token in the CLI config |
Requests send the token as Authorization: Bearer <token> over HTTPS. The token value is shown once at creation and cannot be viewed again. Netlify tokens have no fixed prefix: the open source gitleaks rule netlify-access-token finds them by a nearby "netlify" keyword and a 40 to 46 character value, so detection depends on naming variables clearly. The API allows up to 500 requests per minute and, through the API, 3 deploys per minute and 100 per day; sustained traffic near those limits from one token is worth investigating. See the API Key Management Guide for the general lifecycle.
Admin roles and privileged access
| Role | Relevant rights | Security note |
|---|---|---|
| Owner (Team Owner; Organization Owner on Enterprise) | Team settings, members and roles, full project configuration, delete or transfer projects | A PAT from an Owner can do all of this: keep Owners to a few named people and never use their tokens in CI |
| Developer | Edit project configuration, trigger builds, manage deploys | Can see and change build hooks and non-secret environment variables; project access can be restricted by an Owner |
| Internal Builder | Trigger builds, open pull requests; cannot change environment variables or publish to production | A safer base for automation users that only need to build |
| Git Contributor | No platform access; triggers builds through Git | Their commits still run in your build with your build-time secrets |
| Reviewer | Deploy Preview links and feedback only | No configuration access |
| Billing Admin (Enterprise) | Plan and billing only | No technical access |
- Dedicated automation member: create a named service member with the lowest role the pipeline needs, restricted to the projects it deploys, and issue PATs only from that account. Its tokens then stop working when it is removed, not when a developer leaves.
- Deploys need less than you think: a pipeline that only runs
netlify deployfor one project does not need an Owner token; scope by role and project membership, since PATs have no selectable scopes beyond the SSO checkbox. - SSO as a gate: on SSO teams, require that tokens granted team access are registered in your secrets inventory before the checkbox is used.
Integrations, build hooks and secrets
- Build hooks: the URL has the form
https://api.netlify.com/build_hooks/<id>; a POST starts a build of the hook's branch, and query parameterstrigger_branch,trigger_titleandclear_cache=truechange what runs. The POST body reaches the build asINCOMING_HOOK_BODY, so never feed it into a shell command unvalidated. Keep hook URLs in a secrets manager, not in CMS settings pages or public repositories, and delete and recreate a hook if its URL is exposed. - Unlinking clears credentials: unlinking a repository deletes its deploy keys and build hooks, which is a quick way to clear them during an incident (continuous deployment then stops).
- Git access: prefer the Netlify GitHub App limited to selected repositories over OAuth tokens. For private npm dependencies referenced by URL in
package.json, use read-only tokens (a GitLab deploy token withread_repository, for example) stored as environment variables, never written into the file. - Secret environment variables: mark credentials with Contains secret values (
netlify env:set --secret, oris_secret: truein the API). Values become write-only in the UI, CLI and API, must use explicit deploy contexts and scopes, and cannot use the post processing scope. The flag cannot be removed. Values in thedevcontext stay readable, so never put production credentials there. - Build secret scanning: once a variable is secret, builds scan repository code and build output for its value in plaintext, base64, URI-encoded and multi-line forms, and fail the build on a match. Smart detection (Personal, Pro and Enterprise plans) also looks for likely secrets with no configuration. Use
SECRETS_SCAN_OMIT_KEYS,SECRETS_SCAN_OMIT_PATHSorSECRETS_SCAN_SMART_DETECTION_OMIT_VALUESnarrowly;SECRETS_SCAN_ENABLED=falseturns everything off.
See Environment Variables and .env Files and the Webhook Secrets Security Guide.
Logging and detection
- Team audit log (Pro and Enterprise): at Team settings, Audit log, covering team settings, WAF rule updates, membership changes, project settings and integration updates; projects also have a site audit log. Netlify's documentation does not state that token creation or environment variable reads are logged, so do not rely on the audit log alone for token misuse.
- Deploy history as telemetry: deploys you did not expect, deploys from an unknown source, builds triggered by hooks with odd
trigger_titlevalues, orclear_cachebuilds are the strongest signals of a stolen token or hook URL. - Source and CI scanning: scan repositories, CI logs and container images for
NETLIFY_AUTH_TOKENand build hook URLs. See How to find secrets in CI logs and build artefacts. - Endpoint signals: the CLI stores its token in
config.jsonunderLibrary/Preferences/netlify(macOS),.config/netlify(Linux) orAppData\Roaming\netlify\Config(Windows). Infostealers and malicious packages harvest such files, as the Nx s1ngularity attack did for GitHub, npm and cloud credentials.
Metrics worth tracking: PATs in use with no recorded owner or expiry (target 0); CI pipelines using tokens from human accounts (trending to 0); build hooks not triggered in 90 days; secret variables with readable dev context values; builds where secret scanning was disabled or keys omitted.
Hardening baseline
- Inventory: list every PAT (name, owner, expiry, where stored), authorised applications per user, OAuth applications, build hooks per project and deploy keys.
- Move CI to a dedicated member: create the automation account with the lowest role, issue an expiring PAT, store it in your CI secret store, then delete the human tokens it replaces.
- Enforce SSO and restrict token team access: keep the SAML team checkbox off by default and approve exceptions.
- Mark every credential variable secret: test in a branch deploy first, since secret values need explicit contexts and the flag cannot be undone.
- Prune hooks and keys: delete unused build hooks and deploy keys; prefer the GitHub App with selected repositories.
- Rollback: keep the old token valid until the new one has deployed successfully, then delete it; secret-flagged variables can be recreated with a new name if contexts were set wrongly.
Leak response
- Revoke: delete the PAT under Personal access tokens, Options, Delete personal token, or revoke the Netlify CLI or integration under Authorized applications. If you cannot tell which token leaked, or several did, reset the owner's password to invalidate all their PATs and OAuth tokens at once.
- Clear site credentials: delete and recreate exposed build hooks; regenerate deploy keys and remove the old public key from the Git provider.
- Review what changed: check deploy history, project settings, environment variables, domains and team membership in the audit log for the exposure window, and roll back unexpected deploys.
- Rotate downstream secrets: a token holder with configuration rights could deploy code that reads build-time secrets, so rotate the API keys and database credentials your builds and functions use.
- Replace and record: issue a new expiring token from the automation account and log the incident. See the Secrets Rotation Runbook Template and the Work SaaS API tokens leak guide.
Framework mapping
| Control | NIST CSF 2.0 | NIST SP 800-53 r5 | MITRE ATT&CK |
|---|---|---|---|
| Token inventory, expiry and rotation | PR.AA-01 | IA-5 | T1528 Steal Application Access Token |
| Least privilege roles for automation | PR.AA-05 | AC-6 | None specific |
| Secret variables and build scanning | PR.DS-01 | SC-28 | T1552.001 Credentials In Files |
| Audit log and deploy monitoring | DE.CM-09 | AU-6 | None specific |
| Leak response and revocation | RS.MI-02 | IA-5 | None specific |
Common misconfigurations
- Owner PAT in CI: a token that can delete the team sits in a pipeline variable that dozens of people can read.
- Tokens tied to leavers: automation fails, or worse keeps working, after the owner leaves, depending on whether the account was removed.
- Build hook URL in a public CMS or repository: anyone can trigger builds, burn build minutes or clear caches.
- Production secrets in the
devcontext: they stay readable in the UI and API despite the secret flag. - Disabling secret scanning to fix a build:
SECRETS_SCAN_ENABLED=falseinstead of a narrow omit rule.
Practitioner checklist
- Issue automation PATs only from a dedicated, least privilege member, with an expiry.
- Record every PAT's owner, purpose, storage location and expiry date.
- Keep SSO enforced and grant SAML team access to tokens by exception.
- Mark every credential variable as containing secret values and keep secret scanning on.
- Keep production credentials out of the
devdeploy context. - Store build hook URLs as secrets and delete unused hooks and deploy keys.
- Use the Netlify GitHub App limited to selected repositories.
- Rehearse leak response, including the password reset kill switch and downstream rotation.
Standards and references
- Netlify Docs: Get started with the Netlify API
- Netlify Docs: Get started with Netlify CLI
- Netlify Docs: Secrets Controller
- Netlify Docs: Build hooks
- Netlify Docs: Repository permissions and linking
- Netlify Docs: Roles and permissions
- Netlify Docs: Team audit log
- Gitleaks: Detection rules (netlify-access-token)
Related NHI Mgmt Group resources: Heroku, Netlify and Render PaaS Security Guide · Vercel Tokens Security Guide · API Key Management Guide · SaaS API Token Security Guide