Keeping API Keys Secure

Least-privilege scopes, environment separation, a ten-minute leak response, and rotation that does not take your integration down.

An API key is a password with unusual properties. It is held by machines rather than people, it is long-lived, it never gets a second factor, and it spends most of its life copied into places you will have forgotten about by the time it matters. This guide is about the operational handling of a node4 key once it exists: where to keep it, how to split it, what to do in the minutes after it escapes, and how to replace it without an outage. If you have not created one yet, or want the mechanics of the Bearer header and the scope list, start with authentication and come back.

What can someone actually do with a stolen API key?

The first question about any secret is what it buys an attacker, and for a node4 key the answer is decided by you, at creation time, through its scopes.

A key limited to read scopes leaks information: your proxy inventory, bandwidth consumption patterns, plan and order history, the account email. Unpleasant, and useful reconnaissance for a follow-up attack, but not directly expensive.

Write scopes change the arithmetic. A key holding write:ip-whitelist lets an attacker add their own address to your whitelist. At that point they can push traffic through your proxies with no password at all, spending your metered bandwidth until you notice the entry. One holding write:proxies can trigger a refresh that replaces individual proxies, breaking every consumer pinned to the old addresses mid-run. One holding write:webhooks can point your event delivery at an endpoint they control and quietly collect whatever your account emits from then on.

None of these require sophistication. They are single authenticated HTTP calls, and the time between a key appearing in a public repository and the first automated harvester trying it is measured in minutes, not days.

Grant scopes as if each one cost money

The defense that costs nothing is refusing capabilities up front. When you mint a key, choose the smallest scope set the integration actually exercises:

The tell for over-provisioning is the phrase "just in case". A key with every box ticked because that was easier at creation converts any future leak into control of the whole management plane. If an integration later grows a real need for another scope, mint a second key or replace the first. That small friction is the system working, because it forces the widening to be a decision rather than a default.

Scopes also shrink your incident, not just your risk. When a key escapes, its scope list is a complete inventory of what you have to audit. A read:usage key leaking is a shrug; auditing after an all-scopes key leaks means checking everything.

Where should an API key be stored?

In the environment, injected at deploy time, and never in a file that gets committed.

The classic failure is a key in source control. Git makes it worse than an ordinary file leak, because deleting the key from the current version does not remove it: the value remains in every commit that ever contained it, in every clone and fork, in the CI caches that checked those commits out. Once a repository has been pushed anywhere shared, history rewriting is cosmetic; you cannot recall the copies. A key that has been committed and pushed should simply be treated as burned.

So the key never enters the repository at all. Locally, put it in an environment file that your version control ignores, and confirm the ignore rule actually matches before the first commit, not after. In CI, use the platform's secret store, which injects the value at runtime and masks it in logs. In production, prefer a real secret manager over a file on disk: injection at start-up, access audit, and one authoritative place to change the value.

Two quieter variants of the same leak are worth naming. Debug output that dumps the process environment will print your key into logs, which are then shipped to a logging vendor with much weaker access control than your secret store. And shell history keeps any key you have ever pasted into a curl command line; when testing by hand, read the value from the environment rather than typing the literal.

One key per environment, always

Development, staging, and production should each hold their own key, named accordingly in the dashboard. The name field exists precisely so that prod-billing-recon and laptop-experiments are distinguishable at three in the morning.

The payoff is blast radius. Development environments leak constantly: keys go into scratch files, pastebins, screen shares, and bug reports. If development shares a key with production, every one of those small spills is a production incident. Separated, you revoke the development key with zero customer impact and move on.

Separation also makes revocation decisive rather than terrifying. The reason teams hesitate over a suspicious key is not knowing what will break when it dies. When each key has one well-defined consumer, the answer is known in advance, and the hesitation, which is the most expensive part of most incidents, disappears.

Can I put an API key in a browser or mobile app?

No, and no amount of obfuscation changes that.

Any key that ships to a browser or a mobile app is public. Not "obfuscated", not "hard to find": public. A browser bundle is source code delivered to every visitor, and the network tab shows each request's Authorization header to anyone who opens the developer tools. Mobile binaries are unpacked by hobbyists for sport. Copies persist in CDN caches and web archives long after you ship a fix, which is why "it was only in the bundle for an hour" does not help.

There is no scope safe enough to excuse this. Even a purely read-scoped key exposes your account's details and infrastructure to whoever extracts it. The correct shape is a proxy of your own: the browser talks to your backend, your backend holds the node4 key and makes the API call, and the key never crosses into anything you distribute. If you are building in public (sample repositories, tutorials, screencasts), mint a throwaway key first and revoke it when you are done recording.

What should I do in the first ten minutes after a key leaks?

Speed beats elegance here. In order:

  1. Revoke the key immediately: dashboard, Settings → API Keys, revoke. Do this before the replacement exists. A broken integration is a cheap price for closing the door, and revocation takes effect on the key's next use.
  2. Read the dead key's scope list. It defines the worst case and therefore the audit.
  3. If it held write:ip-whitelist, open your IP whitelist and delete any entry you do not recognize. A foreign entry means someone was riding your proxies without credentials; removing it ends that.
  4. If it held write:webhooks, list your webhook endpoints and remove any you did not register. This is an exfiltration channel, and it survives the key that created it.
  5. If it held write:proxies, check whether refreshes you did not run have replaced any of your proxies, and update consumers if so.
  6. Check usage for the exposure window. A bandwidth spike you cannot attribute tells you the key was exercised, not merely exposed. That changes the incident from precaution to confirmed intrusion.
  7. Assume the neighbors are burned. Secrets travel in groups: the file or repository that leaked the key probably also held proxy credentials, database strings, or other vendors' tokens. Rotate what lived beside it; proxy credential handling is covered in authentication.

Then, calmly, mint the replacement, with a narrower scope list than the key you just lost, since the incident has just told you exactly which scopes it never needed.

How do I rotate an API key without downtime?

Replacing a healthy key on schedule should not interrupt anything, and the way to achieve that is overlap: run old and new keys concurrently for a short window instead of swapping atomically.

  1. Mint the new key with the same scopes as the old one. Both are now valid.
  2. Write the new value into your secret store, and redeploy or restart consumers so they pick it up.
  3. Confirm the cutover: your own deploy logs, or a quick authenticated call from each consumer.
  4. Revoke the old key. If anything breaks at this exact moment, you have found a consumer that was reading the key from somewhere other than your secret store, which is itself a finding worth having.

One sharp edge deserves a warning label: the dashboard's one-click Rotate action revokes the old key in the same instant it mints the successor. That is exactly right for a compromised key, where the priority is killing the old value now. It is exactly wrong for routine rotation, because every consumer fails from that instant until the new value is deployed. For scheduled rotation, use the overlap sequence; reserve the rotate button for incidents.

Keys can also be given an expiry date at creation, and it is worth setting one even generously. An expiring key converts "we should rotate sometime" into a date on a calendar, and a key that quietly stops working in a test environment is how you discover your rotation runbook has a gap, while it is still free to discover.

Finally, prune. Keys that no longer have a consumer should be revoked, not kept "in case": an idle key is standing attack surface with zero offsetting benefit, and a short key list is one you can actually audit.

Habits over heroics

Nothing in this guide is difficult; all of it is easy to postpone. The practical order: scope minimally at creation, one key per environment, values only ever in secret stores, an expiry date on everything, and the leak drill written down before you need it. The API surface these keys unlock is catalogd in the proxy management API reference, and if your keys manage whitelist entries, the operational side of those is in IP whitelisting best practices. All of it can be practiced end to end on the free tier described on the pricing page; a leak drill rehearsed on an account with nothing at stake costs you twenty minutes and nothing else.