From 00ee8f2405dd8e5f1787a42e6c1943a43f90f8c9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 10:36:24 +0000 Subject: [PATCH] Prioritize cf-connecting-ip header for IP resolution Add Cloudflare's cf-connecting-ip as the highest priority header when resolving client IP addresses, ahead of x-forwarded-for and x-real-ip. https://claude.ai/code/session_01GSXb76ipr99cDh7zBnavAu --- app/lib/identifiers.test.ts | 24 ++++++++++++++++++++++++ app/lib/identifiers.ts | 1 + 2 files changed, 25 insertions(+) diff --git a/app/lib/identifiers.test.ts b/app/lib/identifiers.test.ts index 8f6767c2..5fa25807 100644 --- a/app/lib/identifiers.test.ts +++ b/app/lib/identifiers.test.ts @@ -6,6 +6,30 @@ import { identifiers } from "./identifiers"; vi.mock(import("next/headers"), () => ({ headers: vi.fn() })); describe("identifiers", () => { + it("should return IP from cf-connecting-ip header", async () => { + vi.mocked(headers).mockResolvedValue( + new Headers([["cf-connecting-ip", "172.16.0.1"]]), + ); + + const result = await identifiers(); + + expect(result.ip).toBe("172.16.0.1"); + }); + + it("should prefer cf-connecting-ip over x-forwarded-for and x-real-ip", async () => { + vi.mocked(headers).mockResolvedValue( + new Headers([ + ["cf-connecting-ip", "172.16.0.1"], + ["x-forwarded-for", "192.168.1.1"], + ["x-real-ip", "10.0.0.1"], + ]), + ); + + const result = await identifiers(); + + expect(result.ip).toBe("172.16.0.1"); + }); + it("should return IP from x-forwarded-for header", async () => { vi.mocked(headers).mockResolvedValue( new Headers([["x-forwarded-for", "192.168.1.1"]]), diff --git a/app/lib/identifiers.ts b/app/lib/identifiers.ts index 909b05ab..d75918c1 100644 --- a/app/lib/identifiers.ts +++ b/app/lib/identifiers.ts @@ -6,6 +6,7 @@ export async function identifiers() { const headersList = await headers(); return { ip: + headersList.get("cf-connecting-ip") ?? headersList.get("x-forwarded-for")?.split(",")[0] ?? headersList.get("x-real-ip") ?? undefined,