import type { Context } from "hono"; const LOOPBACK = /^(127\.|::1$|::ffff:127\.)/; /** * The client's address, for rate limits. The socket peer, unless the peer is loopback: then it is * a reverse proxy on the same host (Caddy, as Kepr 2 was deployed), and the address is the * rightmost `X-Forwarded-For` entry, the one that proxy appended. Anything to its left came from * the client and could be forged, so it is never trusted. */ export function clientIp(c: Context): string { // Bun passes its server as the env; a test passes a stand-in with the same method. const server = c.env as { requestIP?: (request: Request) => { address: string } | null }; const peer = server?.requestIP?.(c.req.raw)?.address ?? ""; if (!LOOPBACK.test(peer)) return peer; return c.req.header("x-forwarded-for")?.split(",").at(-1)?.trim() || peer; }