APIs power mobile apps, single-page frontends and integrations, and they’re a favourite target, because they expose data and actions directly and are easy to automate against. Most API breaches don’t need clever exploits. They use missing authorization checks, overly generous responses or absent rate limits. Here are the practices that prevent the common failures, many of them from the OWASP API Security Top 10.

1. HTTPS everywhere

Every API endpoint should be HTTPS only, with HSTS. Tokens and credentials in plaintext HTTP can be stolen on any network along the way. See Let’s Encrypt for Nginx and HSTS.

2. Authentication

  • Use established standards: OAuth 2.0 / OpenID Connect for user-delegated access, and API keys or client credentials for server-to-server. See OAuth 2.0 explained.
  • Keep access tokens short-lived (minutes to an hour), with refresh tokens to renew.
  • If you use JWTs, verify the signature and algorithm on every request, check exp, aud and iss, and never accept alg: none. See JWT explained.
  • Send tokens in the Authorization: Bearer … header, not in URLs, which end up in logs, browser history and Referer headers.
  • Store API keys hashed on the server side (like passwords), and allow rotation and revocation.

3. Authorization: check every object, every time

The number one API vulnerability is Broken Object Level Authorization (BOLA). The API checks that you’re logged in, but not that the object you request is yours:

GET /api/orders/1001   ← mine
GET /api/orders/1002   ← someone else's, but the API returns it

Rules:

  • Every handler that loads an object by ID must verify the caller may access that object (WHERE id = ? AND user_id = ?).
  • Check function-level permissions too: can a normal user call DELETE /api/users/… or admin endpoints?
  • Don’t rely on unguessable IDs (UUIDs) as access control. They help, but they aren’t authorization.

4. Return only what’s needed

  • Don’t serialise whole database objects. Explicitly choose response fields. Leaking password_hash, internal flags or other users’ emails is a common finding (“excessive data exposure”).
  • Guard against mass assignment: only accept whitelisted fields on create and update, so clients can’t set is_admin: true.

5. Validate input

  • Validate types, lengths, formats and ranges with a schema (JSON Schema, OpenAPI validators, Pydantic, Zod, Joi).
  • Use parameterised queries or an ORM, never string-built SQL. See SQL joins for the query side.
  • Limit request body sizes (Nginx client_max_body_size, and the framework’s limits). See Nginx 413.
  • GraphQL: limit query depth and complexity, and disable introspection in production if it isn’t needed.

6. Rate limiting and quotas

Without limits, attackers can brute-force logins, scrape data and run up your costs:

  • per-IP and per-token limits, stricter on auth endpoints;
  • 429 Too Many Requests with a Retry-After header;
  • limits on expensive operations (search, exports, AI calls).
limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;
location /api/ {
    limit_req zone=api burst=40 nodelay;
    proxy_pass http://127.0.0.1:3000;
}

See API rate limiting and Nginx rate limiting.

7. CORS, configured narrowly

CORS controls which browser origins may call your API. It isn’t authentication.

  • Allow specific origins (https://app.example.com), not *, for APIs that use cookies or credentials.
  • Never reflect any Origin back while also allowing credentials.

See CORS explained and Nginx CORS.

8. Errors and headers

  • Generic error messages for clients, with details in server logs. No stack traces, SQL errors or framework debug pages in production.
  • Consistent status codes (401 vs 403 vs 404). See HTTP status codes.
  • Security headers: Content-Type set correctly, X-Content-Type-Options: nosniff, and no caching of sensitive responses (Cache-Control: no-store).

9. Inventory and versioning

  • Know every endpoint and version you expose. Old /v1/ endpoints with weaker checks and forgotten debug routes are common holes.
  • Keep an OpenAPI spec, and remove deprecated versions on schedule.
  • Separate admin and internal APIs from public ones. Keep them on private networks where possible.

10. Logging and monitoring

  • Log authentication events, authorization failures, rate-limit hits and admin actions, without logging tokens or passwords.
  • Alert on spikes of 401/403/429 responses, or unusual access patterns (one token reading thousands of objects).
  • Webhooks you receive should be verified with signatures. See webhooks explained.

Quick checklist

Area Check
Transport HTTPS only, HSTS
Auth Standard tokens, short-lived, in headers
Authorization Per-object and per-function checks
Data Explicit response fields, no mass assignment
Input Schema validation, body size limits
Abuse Rate limits, 429 + Retry-After
Browser Narrow CORS
Errors No stack traces in production
Ops Endpoint inventory, logging, alerts

Related: REST vs GraphQL and WAF explained.

  • API
  • Security
  • Web Development