---
# === IDENTITY ===
id: software/security/api-security-checklist/2026
canonical_question: "What is the API security checklist?"
aliases:
  - "API security best practices checklist"
  - "REST API security checklist"
  - "OWASP API security checklist"
  - "how to secure an API"
  - "API authentication authorization checklist"
  - "API hardening guide"
  - "secure API design checklist"
  - "API security audit checklist"
entity_type: software_reference
domain: software > security > API Security Checklist
region: global
jurisdiction: global
temporal_scope: 2023-2026

# === VERIFICATION ===
last_verified: 2026-02-27
confidence: 0.93
version: 1.0
first_published: 2026-02-27

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "OWASP API Security Top 10 2023 edition replaced the 2019 list; OAuth 2.1 draft consolidates best practices (2024)"
  next_review: 2026-08-26
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "NEVER expose API endpoints without authentication unless explicitly designed as public"
  - "ALWAYS enforce HTTPS with TLS 1.2+ -- plaintext HTTP must be rejected, not redirected"
  - "NEVER include secrets (API keys, tokens, passwords) in URLs -- use Authorization header or request body"
  - "Rate limiting MUST be applied per-user/per-key, not just per-IP -- shared NATs make IP-only limiting ineffective"
  - "Input validation MUST happen server-side -- client-side validation is a UX feature, not a security control"
  - "NEVER return stack traces, SQL errors, or internal paths in API error responses"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "Need web application XSS prevention specifically"
    use_instead: "software/security/xss-prevention/2026"
  - condition: "Need OAuth 2.0 implementation details only"
    use_instead: "software/patterns/oauth2-implementation/2026"
  - condition: "Need GraphQL-specific security (not REST)"
    use_instead: "software/security/graphql-security/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: "api_type"
    question: "What type of API are you securing?"
    type: choice
    options: ["REST", "GraphQL", "gRPC", "WebSocket", "SOAP"]
  - key: "auth_method"
    question: "What authentication method are you using or planning to use?"
    type: choice
    options: ["OAuth 2.0/OIDC", "JWT (self-issued)", "API keys", "mTLS", "Basic Auth (needs migration)", "None yet"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/security/api-security-checklist/2026"
suggested_citation: "Source: knowledgelib.io -- AI Knowledge Library (verified 2026-02-27)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "software/security/xss-prevention/2026"
      label: "XSS Prevention Guide"
    - id: "software/patterns/jwt-implementation/2026"
      label: "JWT Implementation Guide"
  solves: []
  alternative_to: []
  often_confused_with:
    - id: "software/security/graphql-security/2026"
      label: "GraphQL Security Guide"
    - id: "software/patterns/oauth2-implementation/2026"
      label: "OAuth 2.0 Implementation Guide"

# === SOURCES (7 authoritative sources) ===
# Types: official_docs, technical_blog, rfc_spec, academic_paper, community_resource, industry_report
# Reliability: high, moderate_high, moderate, moderate_low, low, authoritative
sources:
  - id: src1
    title: "OWASP API Security Top 10 -- 2023"
    author: OWASP Foundation
    url: https://owasp.org/API-Security/editions/2023/en/0x11-t10/
    type: community_resource
    published: 2023-06-05
    reliability: authoritative
  - id: src2
    title: "REST Security Cheat Sheet"
    author: OWASP Foundation
    url: https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html
    type: community_resource
    published: 2024-01-15
    reliability: authoritative
  - id: src3
    title: "API-Security-Checklist"
    author: Shieldfy
    url: https://github.com/shieldfy/API-Security-Checklist
    type: community_resource
    published: 2023-10-01
    reliability: high
  - id: src4
    title: "API Security Checklist 2026"
    author: Wiz
    url: https://www.wiz.io/academy/api-security/api-security-checklist
    type: technical_blog
    published: 2025-11-20
    reliability: moderate_high
  - id: src5
    title: "The Ultimate API Security Checklist for 2025"
    author: GlobalDots
    url: https://www.globaldots.com/resources/blog/api-security-checklist/
    type: technical_blog
    published: 2025-03-10
    reliability: moderate_high
  - id: src6
    title: "Logging and Monitoring -- Securing HTTP-based APIs"
    author: UK National Cyber Security Centre
    url: https://www.ncsc.gov.uk/collection/securing-http-based-apis/6-logging-and-monitoring
    type: official_docs
    published: 2024-09-01
    reliability: high
  - id: src7
    title: "API Security Best Practices Guide for 2026"
    author: Xcitium
    url: https://www.xcitium.com/blog/news/api-security-best-practices/
    type: technical_blog
    published: 2025-12-15
    reliability: moderate_high
---

# API Security Checklist: Comprehensive Hardening Guide

## TL;DR

- **Bottom line**: Secure APIs require layered defenses across authentication, authorization, input validation, rate limiting, output filtering, and monitoring -- no single control is sufficient. [src1]
- **Key tool/command**: `helmet()` (Node.js), OWASP ZAP API scan, or `bearer scan` for automated API security testing.
- **Watch out for**: Broken Object Level Authorization (BOLA) accounts for ~40% of API attacks and is the #1 OWASP API risk -- always verify the requesting user owns the resource. [src1]
- **Works with**: Any HTTP-based API (REST, GraphQL, gRPC-web). Framework-specific examples for Python/FastAPI, Node.js/Express, and Go.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- NEVER expose API endpoints without authentication unless explicitly designed as public
- ALWAYS enforce HTTPS with TLS 1.2+ -- plaintext HTTP must be rejected, not redirected
- NEVER include secrets (API keys, tokens, passwords) in URLs -- use Authorization header or request body
- Rate limiting MUST be applied per-user/per-key, not just per-IP -- shared NATs make IP-only limiting ineffective
- Input validation MUST happen server-side -- client-side validation is a UX feature, not a security control
- NEVER return stack traces, SQL errors, or internal paths in API error responses

## Quick Reference

| # | Vulnerability | OWASP ID | Risk | Vulnerable Pattern | Secure Pattern |
|---|---|---|---|---|---|
| 1 | Broken Object Level Authorization | API1:2023 | Critical | `GET /api/users/123/orders` without ownership check | Verify `req.user.id === resource.owner_id` on every request [src1] |
| 2 | Broken Authentication | API2:2023 | Critical | No rate limit on `/login`; tokens never expire | Rate-limit auth endpoints; use short-lived JWTs + refresh tokens [src1] |
| 3 | Broken Object Property Level Authorization | API3:2023 | High | API returns all fields including `is_admin`, `ssn` | Allowlist response fields per role; block mass assignment [src1] |
| 4 | Unrestricted Resource Consumption | API4:2023 | High | No pagination limit; unbounded file uploads | Enforce `max_page_size`, upload size limits, request timeouts [src1] |
| 5 | Broken Function Level Authorization | API5:2023 | High | `PUT /api/admin/users` accessible to regular users | Check role/permissions per endpoint, not just authentication [src1] |
| 6 | Unrestricted Access to Sensitive Business Flows | API6:2023 | Medium | No bot protection on `/api/checkout` | CAPTCHA, rate limiting, device fingerprinting on critical flows [src1] |
| 7 | Server-Side Request Forgery (SSRF) | API7:2023 | High | `fetch(user_supplied_url)` without validation | Allowlist target hosts; block private IP ranges [src1] |
| 8 | Security Misconfiguration | API8:2023 | Medium | CORS `Access-Control-Allow-Origin: *`; verbose errors | Restrict CORS origins; return generic errors; disable debug mode [src1] |
| 9 | Improper Inventory Management | API9:2023 | Medium | Deprecated `/api/v1/` still active with no auth | Maintain API inventory; decommission old versions; document all endpoints [src1] |
| 10 | Unsafe Consumption of APIs | API10:2023 | Medium | Trusting third-party API responses without validation | Validate and sanitize all external API data before use [src1] |

**Security Headers for API Responses:** [src2]

| # | Header | Value | Purpose |
|---|---|---|---|
| 1 | `Strict-Transport-Security` | `max-age=31536000; includeSubDomains` | Force HTTPS |
| 2 | `X-Content-Type-Options` | `nosniff` | Prevent MIME sniffing |
| 3 | `X-Frame-Options` | `DENY` | Block framing |
| 4 | `Cache-Control` | `no-store` | Prevent caching sensitive responses |
| 5 | `Content-Security-Policy` | `frame-ancestors 'none'` | Block iframe embedding |
| 6 | `Content-Type` | Match actual response format | Prevent type confusion |

## Decision Tree

```
START: What aspect of API security are you implementing?
├── Authentication?
│   ├── Public API with rate limiting only → API keys + rate limiter
│   ├── User-facing API → OAuth 2.0 + PKCE (browser) or client_credentials (service)
│   └── Service-to-service → mTLS or JWT with shared secret
├── Authorization?
│   ├── Resource-level access (BOLA) → Check ownership on EVERY request
│   ├── Function-level access → RBAC/ABAC middleware per endpoint
│   └── Property-level access → Response field allowlist per role
├── Input validation?
│   ├── Structured data (JSON) → JSON Schema validation middleware
│   ├── File uploads → Size limit + type allowlist + virus scan
│   └── Query parameters → Type coercion + range validation
├── Rate limiting?
│   ├── Authentication endpoints → Strict: 5-10 req/min per user
│   ├── Regular endpoints → Moderate: 100-1000 req/min per key
│   └── Public/search endpoints → Generous: 1000+ req/min per IP
├── Logging & monitoring?
│   ├── YES → Structured JSON logs + SIEM integration + alerting
│   └── NO → Implement immediately -- blind spots enable undetected breaches
└── DEFAULT → Start with authentication + HTTPS + rate limiting, then layer remaining controls
```

## Step-by-Step Guide

### 1. Enforce HTTPS and security headers

Reject plaintext HTTP at the load balancer or reverse proxy level. Configure TLS 1.2+ with strong cipher suites. Add security headers to all API responses. [src2]

```nginx
# Nginx: redirect HTTP and set security headers
server {
    listen 80;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers on;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header Cache-Control "no-store" always;
}
```

**Verify**: `curl -sI https://your-api.com/health | grep -i strict-transport` -- should return the HSTS header.

### 2. Implement authentication on all endpoints

Use OAuth 2.0 with PKCE for browser clients, client_credentials for service-to-service, and validate JWTs on every request. Never rely on API keys alone for sensitive operations. [src1] [src2]

```python
# FastAPI: JWT validation middleware
from fastapi import Depends, HTTPException, Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt  # PyJWT ^2.8.0

security = HTTPBearer()
JWT_SECRET = os.environ["JWT_SECRET"]
JWT_ALGORITHM = "RS256"

async def verify_token(credentials: HTTPAuthorizationCredentials = Security(security)):
    try:
        payload = jwt.decode(
            credentials.credentials,
            JWT_SECRET,
            algorithms=[JWT_ALGORITHM],
            audience="your-api",
            issuer="your-auth-server"
        )
        return payload
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token expired")
    except jwt.InvalidTokenError:
        raise HTTPException(status_code=401, detail="Invalid token")
```

**Verify**: `curl -H "Authorization: Bearer invalid" https://your-api.com/resource` -- should return 401.

### 3. Add object-level authorization checks

Check that the authenticated user has permission to access the specific resource on every request. This prevents BOLA, the #1 API vulnerability. [src1]

```python
# FastAPI: BOLA prevention -- verify resource ownership
@app.get("/api/orders/{order_id}")
async def get_order(order_id: str, user=Depends(verify_token)):
    order = await db.fetch_order(order_id)
    if not order:
        raise HTTPException(status_code=404, detail="Not found")
    # CRITICAL: verify the requesting user owns this resource
    if order["user_id"] != user["sub"]:
        raise HTTPException(status_code=403, detail="Forbidden")
    return order
```

**Verify**: Request another user's resource with a valid token -- should return 403, not the resource.

### 4. Validate all input with schemas

Use schema validation to reject unexpected fields, types, and values. Validate Content-Type headers. Enforce size limits. [src2] [src3]

```python
# FastAPI: Pydantic schema validation (built-in)
from pydantic import BaseModel, Field, constr
from fastapi import Request

class CreateOrderRequest(BaseModel):
    product_id: constr(pattern=r'^[a-zA-Z0-9-]{1,50}$')  # Allowlist pattern
    quantity: int = Field(ge=1, le=100)                    # Range validation
    shipping_address: constr(max_length=500)               # Length limit

@app.post("/api/orders")
async def create_order(body: CreateOrderRequest, user=Depends(verify_token)):
    # FastAPI auto-validates; invalid input returns 422
    return await db.create_order(user["sub"], body.dict())
```

**Verify**: Send `{"product_id": "'; DROP TABLE--", "quantity": -1}` -- should return 422.

### 5. Apply rate limiting

Rate-limit per user/API key, not just per IP. Use stricter limits on authentication endpoints. Return 429 with Retry-After header. [src2] [src4]

```python
# FastAPI: rate limiting with slowapi
from slowapi import Limiter
from slowapi.util import get_remote_address

limiter = Limiter(key_func=get_remote_address)

@app.post("/api/auth/login")
@limiter.limit("5/minute")                         # Strict for auth
async def login(request: Request): ...

@app.get("/api/orders")
@limiter.limit("100/minute")                       # Moderate for data
async def list_orders(request: Request, user=Depends(verify_token)): ...
```

**Verify**: Send 6 rapid login requests -- the 6th should return `429 Too Many Requests`.

### 6. Configure structured logging and monitoring

Log all authentication events, authorization failures, and unusual patterns. Use structured JSON format. Never log sensitive data (passwords, tokens, PII). [src6]

```python
# Python: structured security logging
import logging, json
from datetime import datetime, timezone

class SecurityLogger:
    def __init__(self):
        self.logger = logging.getLogger("api.security")

    def log_auth_event(self, event_type, user_id, ip, success, detail=""):
        self.logger.info(json.dumps({
            "timestamp": datetime.now(timezone.utc).isoformat(),
            "event": event_type,
            "user_id": user_id,
            "ip": ip,
            "success": success,
            "detail": detail
            # NEVER log: password, token, session_id
        }))
```

**Verify**: Check logs after a failed login -- should contain structured JSON with event type and IP, but no password or token values.

## Code Examples

### Python/FastAPI: Complete Security Middleware Stack

```python
# Input:  HTTP request to any API endpoint
# Output: Secured response with headers, auth, rate limiting

from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware

app = FastAPI()

# 1. CORS -- restrict origins (never use "*" in production)
app.add_middleware(CORSMiddleware,
    allow_origins=["https://your-app.com"],
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
)

# 2. Security headers middleware
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        response = await call_next(request)
        response.headers["X-Content-Type-Options"] = "nosniff"
        response.headers["X-Frame-Options"] = "DENY"
        response.headers["Cache-Control"] = "no-store"
        response.headers["Strict-Transport-Security"] = "max-age=31536000"
        return response

app.add_middleware(SecurityHeadersMiddleware)
```

### Node.js/Express: Helmet + Rate Limiting + Auth

> Full script: [node-js-express-helmet-rate-limiting-auth.js](scripts/node-js-express-helmet-rate-limiting-auth.js) (27 lines)

```javascript
// Input:  HTTP request to Express API
// Output: Secured response with all hardening applied
const express = require('express');   // ^4.21.0
const helmet = require('helmet');     // ^8.0.0
const rateLimit = require('express-rate-limit');  // ^7.4.0
# ... (see full script)
```

### Go: Chi Router with Auth + Rate Limiting Middleware

> Full script: [go-chi-router-with-auth-rate-limiting-middleware.go](scripts/go-chi-router-with-auth-rate-limiting-middleware.go) (37 lines)

```go
// Input:  HTTP request to Go API
// Output: Secured response with middleware chain
package main
import (
    "net/http"
# ... (see full script)
```

## Anti-Patterns

### Wrong: Relying on API keys as the only authentication

```python
# BAD -- API keys alone are insufficient for user-facing APIs
@app.get("/api/user/profile")
async def get_profile(api_key: str = Query(...)):
    user = await db.find_by_api_key(api_key)
    return user  # API keys are shared secrets, easily leaked in logs/URLs
```

### Correct: OAuth 2.0 tokens with proper validation

```python
# GOOD -- JWT with audience, issuer, and expiry validation
@app.get("/api/user/profile")
async def get_profile(user=Depends(verify_token)):
    return await db.find_by_id(user["sub"])
    # Token is short-lived, validated, and never in URLs
```

### Wrong: Trusting client-provided IDs without ownership check

```javascript
// BAD -- BOLA vulnerability: no ownership verification
app.get('/api/users/:userId/documents', async (req, res) => {
  const docs = await db.getDocuments(req.params.userId);
  res.json(docs);  // Any authenticated user can read any user's documents
});
```

### Correct: Verify resource ownership on every request

```javascript
// GOOD -- check that authenticated user owns the resource
app.get('/api/users/:userId/documents', authenticate, async (req, res) => {
  if (req.user.id !== req.params.userId) {
    return res.status(403).json({ error: 'Forbidden' });
  }
  const docs = await db.getDocuments(req.params.userId);
  res.json(docs);
});
```

### Wrong: Returning all object properties in responses

```python
# BAD -- mass data exposure (API3:2023)
@app.get("/api/users/{user_id}")
async def get_user(user_id: str):
    user = await db.get_user(user_id)
    return user  # Returns password_hash, ssn, is_admin, internal_notes...
```

### Correct: Allowlist response fields per role

```python
# GOOD -- explicit field allowlist per role
PUBLIC_FIELDS = {"id", "name", "avatar_url"}
ADMIN_FIELDS = PUBLIC_FIELDS | {"email", "role", "created_at"}

@app.get("/api/users/{user_id}")
async def get_user(user_id: str, user=Depends(verify_token)):
    record = await db.get_user(user_id)
    fields = ADMIN_FIELDS if user.get("role") == "admin" else PUBLIC_FIELDS
    return {k: v for k, v in record.items() if k in fields}
```

### Wrong: Verbose error messages that leak internals

```javascript
// BAD -- leaks database schema and query details
app.use((err, req, res, next) => {
  res.status(500).json({
    error: err.message,        // "relation 'users' does not exist"
    stack: err.stack,          // Full stack trace with file paths
    query: err.sql             // "SELECT * FROM users WHERE id = $1"
  });
});
```

### Correct: Generic errors externally, detailed logging internally

```javascript
// GOOD -- generic response + internal structured logging
app.use((err, req, res, next) => {
  const errorId = crypto.randomUUID();
  logger.error({ errorId, message: err.message, stack: err.stack,
    path: req.path, method: req.method, userId: req.user?.id });
  res.status(500).json({
    error: 'Internal server error',
    reference: errorId  // Client can report this for support
  });
});
```

## Common Pitfalls

- **BOLA via predictable IDs**: Using sequential integers (1, 2, 3) for resource IDs makes enumeration trivial. Fix: Use UUIDs and always verify ownership regardless of ID format. [src1]
- **JWT `alg: none` attack**: Libraries that accept unsigned JWTs when configured for HMAC/RSA. Fix: Always specify `algorithms` parameter explicitly; never use `alg: none`. [src2]
- **CORS wildcard on authenticated APIs**: `Access-Control-Allow-Origin: *` cannot be combined with `credentials: include`, but misconfigured APIs may reflect the Origin header. Fix: Allowlist specific origins; never echo the `Origin` header blindly. [src2]
- **Rate limiting bypassed via header spoofing**: Trusting `X-Forwarded-For` without verifying the proxy chain lets attackers rotate "IPs". Fix: Configure trusted proxy count; use the rightmost untrusted IP. [src4]
- **Missing pagination limits**: Endpoints returning unbounded results enable denial of service via `?limit=999999`. Fix: Enforce `max_page_size` server-side (e.g., max 100 items). [src1]
- **Stale API versions left running**: Deprecated `/api/v1/` with weaker auth remains accessible. Fix: Maintain an API inventory; decommission and block old versions. [src1]
- **Logging tokens and passwords**: Security logs that include Bearer tokens or request bodies with passwords. Fix: Sanitize logs; redact Authorization headers and sensitive fields. [src6]
- **Third-party API data trusted blindly**: External API responses injected into SQL queries or HTML without validation. Fix: Validate and sanitize all external data the same as user input. [src1]

## Diagnostic Commands

```bash
# Check TLS configuration and certificate
openssl s_client -connect your-api.com:443 -tls1_2 </dev/null 2>/dev/null | head -20

# Check security headers on API response
curl -sI https://your-api.com/api/health | grep -iE 'strict-transport|x-content-type|x-frame|cache-control|content-security'

# Test rate limiting (should get 429 after limit)
for i in $(seq 1 20); do curl -s -o /dev/null -w "%{http_code}\n" https://your-api.com/api/auth/login -X POST -d '{}'; done

# Scan API with OWASP ZAP
docker run -t ghcr.io/zaproxy/zaproxy:stable zap-api-scan.py \
  -t https://your-api.com/openapi.json -f openapi -r report.html

# Check for exposed debug endpoints
curl -s https://your-api.com/api/debug 2>/dev/null && echo "WARNING: debug endpoint exposed"
curl -s https://your-api.com/api/v1/docs 2>/dev/null | head -5

# Test CORS misconfiguration
curl -sI -H "Origin: https://evil.com" https://your-api.com/api/data | grep -i access-control

# Check for BOLA: request resource belonging to another user
curl -s -H "Authorization: Bearer YOUR_TOKEN" https://your-api.com/api/users/OTHER_USER_ID/data

# Audit JWT configuration
echo "YOUR_JWT" | cut -d. -f1 | base64 -d 2>/dev/null | python3 -m json.tool
```

## Version History & Compatibility

| Standard | Version | Status | Key Changes |
|---|---|---|---|
| OWASP API Security Top 10 | 2023 | Current | Replaced 2019 list; added SSRF, unsafe consumption, business flow abuse |
| OWASP API Security Top 10 | 2019 | Superseded | Original list; mass assignment and injection as separate items |
| OAuth 2.0 | RFC 6749 | Current | Authorization framework; use with PKCE (RFC 7636) |
| OAuth 2.1 | Draft | In progress | Consolidates PKCE, deprecates implicit flow, enforces exact redirect URI match |
| JWT | RFC 7519 | Current | Use with RS256 or ES256; avoid HS256 with shared secrets in distributed systems |
| TLS | 1.3 (RFC 8446) | Recommended | Faster handshake; removed insecure cipher suites |
| TLS | 1.2 (RFC 5246) | Minimum required | Disable 1.0 and 1.1; deprecated by RFC 8996 |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Building any HTTP API exposed to the internet | Internal-only service on a trusted private network | Network-level access controls may suffice (still apply auth) |
| API handles user data, payments, or PII | Fully public read-only data with no write operations | API keys for rate limiting + HTTPS may be sufficient |
| Microservice-to-microservice communication | Single monolith with no external API surface | Standard web app security practices |
| API consumed by third-party developers | Prototype/proof-of-concept in isolated environment | Prioritize, but accept temporary risk with plan to harden |

## Important Caveats

- This checklist covers HTTP-based REST APIs primarily; GraphQL and gRPC have additional attack surfaces (query depth, field-level auth, streaming abuse) that require specific controls
- API gateways (Kong, AWS API Gateway, Cloudflare) provide centralized rate limiting and auth, but are not a substitute for application-level authorization checks -- BOLA must be checked in your code
- Rate limiting by IP address alone is insufficient in environments with shared NAT, corporate proxies, or IPv6 privacy extensions -- always layer with per-user/per-key limits
- OAuth 2.1 (draft) deprecates the implicit grant flow and requires PKCE for all public clients -- plan to migrate if using implicit flow
- mTLS provides strong service-to-service authentication but adds operational complexity (certificate management, rotation) -- use a service mesh like Istio or Linkerd to simplify
- Compliance requirements (PCI-DSS, HIPAA, GDPR) may impose additional API security controls beyond this checklist -- consult regulation-specific guides

## Related Units
<!-- Generated from related_kos frontmatter -->

- [XSS Prevention Guide](/software/security/xss-prevention/2026)
- [JWT Implementation Guide](/software/patterns/jwt-implementation/2026)
- [GraphQL Security Guide](/software/security/graphql-security/2026)
- [OAuth 2.0 Implementation Guide](/software/patterns/oauth2-implementation/2026)
