API Security Best Practices for Production APIs
HTTPS and a valid JWT only prove the front door locked. Many real API incidents happen after authentication succeeds: a user changes /orders/42 to /orders/99 and reads someone else's data; an internal URL fetch hits cloud metadata; a long-lived signing key never rotates.
This guide is the production security layers article — identity and authorization, exposure, secrets and input, traffic control, third-party/egress hardening, and visibility. For how credentials travel on the wire, see REST authentication methods. For sessions vs JWT vs OAuth, see JWT vs Session vs OAuth. For limiter algorithms and 429 contracts, see rate limiting.
Skimmer: six layers
Treat API security as connected layers, not a flat checklist: (1) identity + object/function/field authorization, (2) minimize exposure (scopes, fields, TLS/mTLS), (3) secrets + input validation, (4) traffic control (rate limits, sensitive-flow defenses), (5) egress + hard defaults, (6) inventory + detection. Skipping one usually undermines the others.
Start with object-level authorization (BOLA/IDOR) — it is repeatedly the top category in the OWASP API Security Top 10 — then close SSRF/egress, then make sure you can detect failures in minutes. Authentication alone never answers "may this caller touch this record?"
Quick reference
- AuthN ≠ AuthZ — check object ownership on every request.
- Least privilege on tokens and response fields.
- Secrets in a manager with rotation — not only env files.
- 429 + Retry-After at the edge; stricter limits on login/OTP.
- Egress allowlists block SSRF into link-local metadata.
- Registry + audit logs + alerts turn breaches into incidents you see.
Remember this
An IDOR that lets any authenticated user read another user's records is a bigger risk than a slightly weaker cipher suite — fix object-level authorization before polishing crypto.
Minimize Exposure: Scopes, Data, and Transport
Least privilege applies to actions and to data returned. Serializing an entire internal record — salary flags, internal IDs, role bits — because it was convenient leaks through misconfigured clients, logging pipelines, and browser extensions. Scope tokens narrowly (a mobile app reading order status does not need cancel rights), and filter response payloads to what the caller needs.
TLS at the edge is not end-to-end encryption. Traffic between the gateway and internal services often runs unencrypted on the assumption that the internal network is trusted — the assumption that turns one compromised host into lateral access. Mutual TLS between services authenticates both sides with certificates. Rotate certificates automatically; a manually tracked expiry is an outage waiting to happen.
Quick reference
- Read-only scopes must not write or delete.
- Filter response fields; treat over-exposure as a bug class in CI schema checks.
- mTLS for service-to-service once you leave a single process.
- Automate certificate rotation.
- TLS termination at the gateway ≠ encryption all the way through.
Remember this
Least privilege covers tokens and payloads — TLS at the edge is not the whole story.
Protect Secrets and Validate Every Input
Signing keys and client secrets let attackers forge tokens or impersonate clients. Prefer a secrets manager (ideally HSM-backed) over long-lived secrets in repos or unchecked env files. Rotation matters: a never-rotated leaked secret stays valid forever.
Validate schemas at the edge — unknown fields, oversized bodies, and bad types should get a generic 400 before business logic or the database. Unbounded payloads are a resource-exhaustion vector, not only a validation nuisance.
Quick reference
- Store secrets in Vault/KMS/etc.; audit read access.
- Rotate on a schedule, not only after incidents.
- Generic 400s — no stack traces in production.
- Set explicit max body size at the gateway.
Remember this
Secrets need rotation and access control; reject bad input before trusted code runs.
Control Traffic and Defend Sensitive Flows
Rate limits, payload caps, and timeouts stop one client — malicious or buggy — from degrading the API. Enforce them at the gateway before application work. Generic limits protect availability; login, checkout, signup, and OTP need tighter velocity rules, idempotency keys, and sometimes step-up MFA.
Use a clear 429 body and Retry-After so legitimate clients back off. Algorithm choices (token bucket vs sliding window) are covered in rate limiting.
Quick reference
- Edge enforcement before app code.
- Stricter limits on /login and OTP than on public reads.
- Idempotency keys on payment/checkout.
- CAPTCHA and step-up MFA as risk-based controls, not the only control.
Remember this
Generic limits protect uptime; sensitive flows need their own velocity and idempotency rules.
Contain Third-Party Risk and Harden Defaults
Outbound calls are an attack surface. SSRF occurs when your API fetches a user-supplied URL and an attacker points it at link-local cloud metadata (169.254.x.x) or internal admin ports — potentially extracting credentials your own host can reach. An egress gate that allowlists approved destinations, blocks internal/link-local ranges by default, and validates partner response size/shape closes that path without trusting every URL a user or partner supplies.
Deny by default on methods, origins, and routes. Lock CORS to known origins in production. Force debug mode and verbose errors off — a stack trace, open OPTIONS response, or leftover debug endpoint is a map of your internals.
Quick reference
- Egress allowlist — do not fetch arbitrary URLs server-side.
- Block 169.254.0.0/16 and private/link-local ranges by default.
- Validate partner responses before trusting them.
- Explicit allow-lists for HTTP methods and CORS origins.
- Wildcard CORS and debug mode in production are common, cheap misconfigurations.
Remember this
Egress allowlists stop SSRF; deny-by-default config stops many "no exploit needed" breaches.
Inventory, Log, Detect, and Respond
You cannot secure an API you do not know exists. Shadow endpoints — deployed without the same review as the rest of the system — and forgotten /v1/ routes still reachable after "deprecation" are recurring incident sources. A registry of APIs, versions, and status (active, deprecated, sunset) turns "we did not know that was still running" into a dashboard item before it becomes an incident.
Prevention eventually fails somewhere — detection should be minutes, not months. Audit authN/authZ decisions, admin actions, and config changes specifically (not only generic access logs). Feed them to alerting on anomalies — spikes in 401/403, unusual admin activity — so logging is detection, not only a compliance checkbox.
Quick reference
- Live inventory of every API version and status, including shadow finds.
- Audit logs for auth decisions and admin/config changes.
- Alert on auth failure spikes and unusual admin patterns.
- Retire old versions with a measured window — do not leave them forever.
- A log nobody reads until after the breach is not providing detection.
Remember this
Inventory and alerting turn eventual failure into a short detection window.
Key takeaway
These layers cover different failure modes: strong auth without object checks still leaks data; strict rate limits without egress controls still allow SSRF; perfect encryption without logs still leaves you learning about breaches from customers. Prioritize object-level authorization, then SSRF/egress, then detection — then deepen the rest.
Practice (30 min): With two test users, attempt to read user A's order as user B before and after an ownership check. Add a 429 with Retry-After on /login. Move one hard-coded secret into an env/secrets load path and confirm it never appears in logs.
Related Articles
Explore this topic