@@ -35,7 +35,8 @@ const encoder = new TextEncoder();
35
35
36
36
function getUserRequest (
37
37
request : Request < unknown , IncomingRequestCfProperties > ,
38
- env : Env
38
+ env : Env ,
39
+ clientIp : string | undefined
39
40
) {
40
41
// The ORIGINAL_URL header is added to outbound requests from Miniflare,
41
42
// triggered either by calling Miniflare.#dispatchFetch(request),
@@ -89,15 +90,6 @@ function getUserRequest(
89
90
// special handling to allow this if a `Request` instance is passed.
90
91
// See https://github.com/cloudflare/workerd/issues/1122 for more details.
91
92
request = new Request ( url , request ) ;
92
- if ( request . cf === undefined ) {
93
- const cf : IncomingRequestCfProperties = {
94
- ...env [ CoreBindings . JSON_CF_BLOB ] ,
95
- // Defaulting to empty string to preserve undefined `Accept-Encoding`
96
- // through Wrangler's proxy worker.
97
- clientAcceptEncoding : request . headers . get ( "Accept-Encoding" ) ?? "" ,
98
- } ;
99
- request = new Request ( request , { cf } ) ;
100
- }
101
93
102
94
// `Accept-Encoding` is always set to "br, gzip" in Workers:
103
95
// https://developers.cloudflare.com/fundamentals/reference/http-request-headers/#accept-encoding
@@ -107,6 +99,18 @@ function getUserRequest(
107
99
request . headers . set ( "Host" , url . host ) ;
108
100
}
109
101
102
+ if ( clientIp && ! request . headers . get ( "CF-Connecting-IP" ) ) {
103
+ const ipv4Regex = / (?< ip > .* ?) : \d + / ;
104
+ const ipv6Regex = / \[ (?< ip > .* ?) \] : \d + / ;
105
+ const ip =
106
+ clientIp . match ( ipv6Regex ) ?. groups ?. ip ??
107
+ clientIp . match ( ipv4Regex ) ?. groups ?. ip ;
108
+
109
+ if ( ip ) {
110
+ request . headers . set ( "CF-Connecting-IP" , ip ) ;
111
+ }
112
+ }
113
+
110
114
request . headers . delete ( CoreHeaders . PROXY_SHARED_SECRET ) ;
111
115
request . headers . delete ( CoreHeaders . ORIGINAL_URL ) ;
112
116
request . headers . delete ( CoreHeaders . DISABLE_PRETTY_ERROR ) ;
@@ -343,6 +347,22 @@ export default <ExportedHandler<Env>>{
343
347
async fetch ( request , env , ctx ) {
344
348
const startTime = Date . now ( ) ;
345
349
350
+ const clientIp = request . cf ?. clientIp as string ;
351
+
352
+ // Parse this manually (rather than using the `cfBlobHeader` config property in workerd to parse it into request.cf)
353
+ // This is because we want to have access to the clientIp, which workerd puts in request.cf if no cfBlobHeader is provided
354
+ const clientCfBlobHeader = request . headers . get ( CoreHeaders . CF_BLOB ) ;
355
+
356
+ const cf : IncomingRequestCfProperties = clientCfBlobHeader
357
+ ? JSON . parse ( clientCfBlobHeader )
358
+ : {
359
+ ...env [ CoreBindings . JSON_CF_BLOB ] ,
360
+ // Defaulting to empty string to preserve undefined `Accept-Encoding`
361
+ // through Wrangler's proxy worker.
362
+ clientAcceptEncoding : request . headers . get ( "Accept-Encoding" ) ?? "" ,
363
+ } ;
364
+ request = new Request ( request , { cf } ) ;
365
+
346
366
// The proxy client will always specify an operation
347
367
const isProxy = request . headers . get ( CoreHeaders . OP ) !== null ;
348
368
if ( isProxy ) return handleProxy ( request , env ) ;
@@ -356,7 +376,7 @@ export default <ExportedHandler<Env>>{
356
376
const clientAcceptEncoding = request . headers . get ( "Accept-Encoding" ) ;
357
377
358
378
try {
359
- request = getUserRequest ( request , env ) ;
379
+ request = getUserRequest ( request , env , clientIp ) ;
360
380
} catch ( e ) {
361
381
if ( e instanceof HttpError ) {
362
382
return e . toResponse ( ) ;
0 commit comments