Skip to content

Commit a346390

Browse files
fix(proxy): resolve client IP from CDN/proxy headers before forwarding to Plausible
1 parent 302c8cc commit a346390

File tree

1 file changed

+34
-2
lines changed

1 file changed

+34
-2
lines changed

src/runtime/server/event-handler.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import type { H3Event } from 'h3'
12
import type { ModuleOptions } from '../../module'
23
import { useRuntimeConfig } from '#imports'
3-
import { createError, defineEventHandler, getRequestIP, proxyRequest } from 'h3'
4+
import { createError, defineEventHandler, getRequestHeader, getRequestIP, proxyRequest } from 'h3'
45
import { joinURL } from 'ufo'
56

67
export default defineEventHandler((event) => {
@@ -16,9 +17,10 @@ export default defineEventHandler((event) => {
1617

1718
try {
1819
const target = joinURL(options.apiHost, 'api/event')
20+
const clientIP = resolveClientIP(event)
1921
return proxyRequest(event, target, {
2022
headers: {
21-
'X-Forwarded-For': getRequestIP(event, { xForwardedFor: true }),
23+
...(clientIP ? { 'X-Forwarded-For': clientIP } : {}),
2224
},
2325
})
2426
}
@@ -31,3 +33,33 @@ export default defineEventHandler((event) => {
3133
})
3234
}
3335
})
36+
37+
/**
38+
* Resolve the real client IP address from common reverse proxy and CDN headers.
39+
*
40+
* This reads headers directly instead of relying on `getRequestIP` alone,
41+
* because `getRequestIP` checks `event.context.clientAddress` first, which
42+
* may be set to an internal/proxy IP (e.g. Docker network IP) by the runtime,
43+
* causing the real client IP from proxy headers to be ignored.
44+
*/
45+
function resolveClientIP(event: H3Event): string | undefined {
46+
// Cloudflare
47+
const cfConnectingIp = getRequestHeader(event, 'cf-connecting-ip')
48+
if (cfConnectingIp)
49+
return cfConnectingIp
50+
51+
// Common reverse proxy header (Nginx, etc.)
52+
const xRealIp = getRequestHeader(event, 'x-real-ip')
53+
if (xRealIp)
54+
return xRealIp
55+
56+
// Standard proxy header (first IP is the original client)
57+
const xForwardedFor = getRequestHeader(event, 'x-forwarded-for')
58+
if (xForwardedFor) {
59+
const firstIp = xForwardedFor.split(',')[0]?.trim()
60+
if (firstIp)
61+
return firstIp
62+
}
63+
64+
return getRequestIP(event)
65+
}

0 commit comments

Comments
 (0)