API Returns 429 Behind a CDN? Trace Rate Limits and Configure Retry Backoff
Create Time:2026-10-15 12:08:10
浏览量
1004

When an API starts returning 429 Too Many Requests after moving behind a CDN, identify the component that generated the response before changing a threshold. The request may pass through CDN security rules, an API gateway, and the application. Each can apply its own limits. Increasing a CDN threshold will not fix a quota enforced by the application.

This guide is for developers and operators responsible for website APIs, public endpoints, and mobile backends. Start with one read-only request and existing logs. You do not need to flood a production endpoint to discover its limit.

What a 429 response tells you

RFC 6585 defines 429 as a response to too many requests within a period of time. The response may include Retry-After, but that header is optional. The standard does not prescribe how a service identifies a caller or counts requests.[1]

Use the symptoms to narrow the investigation:

SymptomWhat to inspect first
One endpoint fails after a burst while others workPath matching, HTTP methods, and endpoint-specific rules
Several accounts on the same office network fail togetherA shared public IP being counted as one caller
The same API key fails from different networksAccount, tenant, key quota, or another shared counter
The CDN logs a blocking event with no matching origin requestEdge enforcement, while also checking log completeness
The CDN forwards the request and the gateway or application records 429Gateway quotas, application limits, or upstream dependencies

These observations are clues. Response headers, error page styling, and request IDs can help, but an intermediary may add or rewrite them. Correlate the same request across the relevant layers before assigning the cause.

Capture a response and follow its path

Choose an existing failed request and record its timestamp and time zone, hostname, path, HTTP method, status, response headers, error body, and request ID. Remove credentials, cookies, personal information, and secret query parameters from diagnostic records.

If another sample is needed, send one request to a read-only endpoint you manage. The following example uses curl on Linux, macOS, or WSL. In Windows PowerShell, use curl.exe and adjust line continuation for that shell.

curl --silent --show-error \
  --connect-timeout 5 --max-time 15 \
  --dump-header response-headers.txt \
  --output response-body.txt \
  --write-out 'status=%{http_code} total=%{time_total}s\n' \
  'https://api.example.com/v1/status'

Replace the URL with your actual read-only endpoint. The command neither retries nor follows redirects automatically, so it preserves the result of this request. A 401 or 403 from an unauthenticated request does not reproduce an authenticated user's problem and cannot rule out rate limiting.

Then inspect the existing records in order:

  1. CDN and security events. Match the timestamp, path, caller, and request ID. Identify the rule, matching conditions, and action. Determine whether the request was forwarded.

  2. API gateway or load balancer. Check the returned status and whether it was generated locally or passed through from an upstream service.

  3. Application and dependencies. Review quota usage, limiter logs, and errors from dependent services. An application may translate a third-party quota error into its own 429 response.

If the origin has no matching record, also check time zones, sampling, the node being queried, and ingestion delays. An unsuccessful log search alone does not establish that the CDN blocked the request.

For a comparison that bypasses the CDN, use an existing controlled diagnostic route. Preserve the Host header, TLS behavior, authentication, and request parameters. Avoid opening the origin to the public internet or disabling site-wide protection for this investigation.

Check which callers share a counter

Once you identify the rule, inspect its counting key.

An IP-based rule can group many legitimate users behind a corporate, campus, or mobile network's shared public address. An application that sees only a reverse proxy's connection address may also count unrelated users together. Check how the real client address is forwarded and parsed, and accept forwarding headers only from trusted proxies. Trusting arbitrary public X-Forwarded-For values allows callers to spoof the identity used by the limiter.

For account or API-key limits, look for shared credentials. Test scripts, scheduled jobs, and production services may all consume the same quota. Separate their usage in monitoring and, where appropriate, give them distinct credentials and budgets before simply raising the shared limit.

Also review path and method matching. A rule intended to protect login submissions may accidentally cover the whole /api/ prefix, including status and list requests.

CDN implementations differ in counter scope and update timing. Cloudflare documents that its rate limiting does not guarantee an exact number of requests reaching the origin: enforcement can lag behind request detection and counter updates. Treat an edge threshold as a control setting, not a precise statement of origin capacity.[3]

Before changing a rule, save its conditions, scope, thresholds, and current observations. Prefer correcting overly broad matching, fixing caller identity, or reducing client concurrency. If a higher threshold is justified by origin capacity, validate a limited change and retain a way to restore the original configuration.

Honor Retry-After within a bounded retry policy

Retry-After accepts either a non-negative number of seconds or an HTTP date. Handle both forms. An HTTP date depends on the client's clock, so account for clock skew; missing, malformed, or expired values should not be treated as a valid future wait.[2]

For example:

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Cache-Control: no-store
Content-Type: application/json

The 30-second value illustrates the syntax. It is not a universal recommendation; the server should choose a value that reflects its recovery conditions. RFC 6585 prohibits caching 429 responses. The example also makes the storage policy explicit with no-store.[1]

Use capped exponential backoff with random jitter to spread retries. The following is a design example, with parameters that must be adapted to the endpoint's latency requirements:

At most 3 attempts, including the initial request
Overall budget: 20 seconds; each request also has its own timeout
Only one layer owns retries

After a 429 that is eligible for retry:
  Set n=0 for the first retry; increment for subsequent retries
  backoff = random(0, min(4 seconds, 0.5 seconds * 2^n))
  If Retry-After is valid: delay = max(Retry-After, backoff)
  Otherwise: delay = backoff
  If delay consumes the remaining budget:
    Stop and return an identifiable rate-limit outcome to the caller
  Wait for delay, then make the next attempt within the remaining budget

The backoff cap applies to the client's calculated backoff. It must not shorten a valid server-directed wait. If the server requests 30 seconds and the call has only 10 seconds left, end the current call rather than reducing the delay to four seconds and retrying early. Count waiting time toward the overall budget, and let the caller cancel the wait.

Random jitter can still produce a very short delay. Combine it with concurrency limits, bounded queues, and a retry budget. AWS's engineering guidance recommends choosing one point in a call stack to retry and using jitter to reduce synchronized bursts.[4]

If a client, gateway, and application SDK each allow up to three attempts, one logical operation can produce up to 27 downstream requests. This is an illustrative multiplication of three attempt limits, not the default behavior of any named product. Include SDK policies when auditing the full retry path.

Treat POST and other writes separately

Read-only GET endpoints can use automatic retries within explicit attempt, timeout, and budget limits. Payments, orders, message submission, and job creation require a review of the operation's idempotency guarantees first.

RFC 9110 advises against automatically retrying non-idempotent methods unless the client knows the operation is effectively idempotent or can determine that the original request was never applied.[2] A 429 response does not replace that review, especially when proxies or application code transform upstream responses.

If an endpoint supports idempotency keys, subsequent attempts for one logical operation must reuse the same key and business parameters under the server's contract. A new key on every attempt may identify each attempt as a separate operation. Check key retention, concurrent handling, and how to query an operation's result after a timeout. Adding an Idempotency-Key header alone does not create server-side deduplication.

For work that can wait longer, use a controlled queue to schedule later attempts. Interactive calls should return a useful retry-later message and stop endless automatic refreshes.

Validate recovery without moving the overload downstream

After making a change, validate it with a small number of authorized business requests. Avoid generating a large production burst. Observe at least a full rate-limit window and a real business traffic peak; the required observation period depends on the rule and workload.

Check legitimate-user recovery, continued enforcement against abnormal traffic, origin load and errors, and the actual request count generated by retries. If 429 responses fall while origin timeouts rise, the change has shifted pressure downstream. Reduce concurrency or restore the earlier threshold as appropriate.

Also check for duplicate SDK or browser submissions, custom caches incorrectly reusing error responses, and correct handling of a Retry-After value longer than the call budget. Keep the configuration, change timestamp, and before-and-after observations together so the effect of each adjustment can be assessed.

A completed investigation should establish which layer returned 429, which identity and window it counted, and how the caller stops or retries within its budget. Those findings determine whether to adjust an edge rule, separate a shared quota, or change client concurrency and queueing.

Official references

References checked on October 15, 2026. Commands, delays, and attempt limits are diagnostic or design examples. This article does not claim production load testing.

  1. RFC 6585, Section 4: 429 Too Many Requests

  2. RFC 9110: Idempotent methods and Retry-After

  3. Cloudflare: Rate limiting rules

  4. AWS: Timeouts, retries, and backoff with jitter