Skip to content

Per-IP HTTP rate limit returning the contract's rate_limited error #104

Description

@MrAlders0n

Problem. /api/v1/* has no rate limiting at all (middleware chain router.go:73-78). The API contract defines a rate_limited error code (api_contract.md:786) that nothing emits. Third parties are reverse-proxying the API in bulk. beacon-web (dev, 2026-09-02) already handles a 429 with Retry-After, never retries 4xx, and shows a RATE LIMITED badge.

Ask.

  • github.com/go-chi/httprate in the global chain, after the client-IP middleware (see the trusted-proxies issue) and before Logger, so 429s are logged with the real IP.
  • Config block ratelimit: { enabled, requests_per_minute, burst }. Start generous, e.g. 300/min per IP on /api/v1/*; the edge logs show a real session peaks at ~150 requests in its first 30 s. In-memory is fine for a single instance; httprate-redis is a drop-in later.
  • Custom httprate.WithLimitHandler that writes the contract envelope {"error":{"code":"rate_limited","message":"..."}} with Content-Type: application/json, Retry-After in delta-seconds, and Access-Control-Expose-Headers: Retry-After (only matters cross-origin, e.g. Vite dev on 5173 without the proxy). Note respondError derives the code from the status text, which would give too_many_requests, so the handler must set the code itself.
  • One log line per 429 with IP and path. The edge fail2ban beacon-api-429 jail keys on the edge log's status, so nothing else is needed there.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew feature or requestp2-mediumMedium prioritysecuritySecurity vulnerability or concern

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions