Files
Flash5/flash-extensions/flash-ext-limiter/docs/http-headers.md
T
Zakaria El OrcheandClaude Opus 5 439588c19f feat(ext-openapi): pick the page that reads the document
Swagger UI, Redoc or Scalar, each configured with its own options under the
names its own documentation gives them, or no page at all. What a UI does not
name still passes through, so a bundle's whole option set stays reachable
without this extension tracking it.

The page is rendered once at boot and the document is encoded once per
revision, so a request to any of the three routes hands out bytes rather than
building them: the spec used to be serialized again on every single request.

/openapi/swagger becomes /openapi/docs, because the path names what is served
and not which bundle happens to serve it. That page also named a preset that
lives in a bundle it never loaded, and BaseLayout never needed it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 12:03:48 +00:00

3.9 KiB

HTTP headers and 429 response

The extension injects standard rate-limit headers on every request — both allowed and rejected. Clients can use these headers to implement back-off logic without waiting for a 429.

Response headers

Header Type Description
X-RateLimit-Limit integer Maximum requests allowed in the current window
X-RateLimit-Remaining integer Requests remaining in the current window (≥ 0)
X-RateLimit-Reset Unix timestamp (s) When the quota resets or the next token arrives
Retry-After seconds Only on 429 — how long to wait before retrying (≥ 1)

Example — allowed request

HTTP/1.1 200 OK
X-RateLimit-Limit:     100
X-RateLimit-Remaining: 73
X-RateLimit-Reset:     1711750860
Content-Type: application/json

Example — rejected request (429)

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit:     100
X-RateLimit-Remaining: 0
X-RateLimit-Reset:     1711750860
Retry-After:           1
Content-Type: text/plain

Too Many Requests

Header semantics by strategy

FIXED_WINDOW

Header Value
X-RateLimit-Reset Unix timestamp of the next window start (aligned to clock)
Retry-After Seconds until X-RateLimit-Reset (minimum 1)

At a 1-second window boundary Retry-After will typically be 1.

TOKEN_BUCKET

Header Value
X-RateLimit-Remaining Current token count (may increase between requests due to refill)
X-RateLimit-Reset Estimated Unix timestamp when the next token arrives
Retry-After Milliseconds-precise estimate converted to seconds (minimum 1)

Because the token bucket refills continuously, X-RateLimit-Reset is a near-future timestamp rather than an aligned window boundary.

Retry-After precision

Retry-After is computed as:

retryAfter = max(1, X-RateLimit-Reset - currentTimeSeconds)

The minimum value is always 1 second — RFC 7231 discourages Retry-After: 0 as it encourages instant retry loops.

Client-side back-off example (Java)

HttpResponse<String> res = client.send(request, BodyHandlers.ofString());

if (res.statusCode() == 429) {
    String retryAfter = res.headers().firstValue("Retry-After").orElse("1");
    long waitMs = Long.parseLong(retryAfter) * 1000L;
    Thread.sleep(waitMs);
    // retry...
}

Client-side back-off example (JavaScript fetch)

const res = await fetch('/api/search?q=flash');

if (res.status === 429) {
    const retryAfter = parseInt(res.headers.get('Retry-After') ?? '1', 10);
    await new Promise(r => setTimeout(r, retryAfter * 1000));
    // retry...
}

Monitoring / alerting

X-RateLimit-Remaining can be scraped by a metrics agent to track approaching limits before they hit 429:

  • remaining / limit < 0.1 → warning (less than 10% quota left)
  • status == 429 → rate-limit violation counter increment

If flash-ext-limiter is used together with a future metrics extension, the 429 rate per resolver key is a natural signal for abuse detection or auto-scaling.

Header injection timing

Headers are injected before calling next.handle(req, res) on allowed requests, and instead of calling it on rejected requests. This means:

  • Handlers cannot accidentally overwrite X-RateLimit-* headers (they are set first, but handlers that call res.header(...) with the same name will add a second value — avoid this by not setting these headers manually).
  • On 429, the handler body is never executed — no side effects occur.

Integration with OpenAPI (flash-ext-openapi)

When flash-ext-openapi is installed, handlers annotated with @Limit automatically contribute rate-limit response headers to generated OpenAPI responses:

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset
  • Retry-After on 429

If 429 is not manually declared, OpenAPI auto-adds 429 Too Many Requests.