diff --git a/src/lib/http/client-ip.ts b/src/lib/http/client-ip.ts new file mode 100644 index 0000000..ed51791 --- /dev/null +++ b/src/lib/http/client-ip.ts @@ -0,0 +1,23 @@ +/** + * Best-effort client IP for rate-limit keying. + * + * This is NOT used for authentication, logging, or any decision that needs to + * be accurate. It is a tie-breaker so two clients hammering the same token + * from different networks each get their own budget. The spec deliberately + * does not persist it. + * + * Reads `x-forwarded-for` first (the only header a load balancer or reverse + * proxy should be trusted to set), then `x-real-ip`, and falls back to + * "unknown" so all unrecognised callers share one bucket rather than each + * getting their own unlimited budget. + */ +export function clientIpFromHeaders(headers: Headers): string { + const fwd = headers.get("x-forwarded-for"); + if (fwd) { + const first = fwd.split(",")[0]?.trim(); + if (first) return first; + } + const real = headers.get("x-real-ip"); + if (real) return real.trim(); + return "unknown"; +}