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