Skip to content

Commit b23cef8

Browse files
authored
Refactor IP address handling in server.js
Refactor IP address handling for better clarity and efficiency. Improved comments and streamlined logic for determining the user's real IP address.
1 parent 3677815 commit b23cef8

1 file changed

Lines changed: 19 additions & 80 deletions

File tree

server-src/server.js

Lines changed: 19 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ function isPrivateIp(ip) {
8888
if (!ip) return false;
8989

9090
// Clean up Node's IPv6-mapped IPv4 prefix (::ffff:)
91-
const cleanIp = ip.replace(/^::ffff:/, '');
91+
const cleanIp = ip.replace(/^::ffff:/, '').trim();
9292

9393
if (cleanIp === '::1' || cleanIp === 'localhost') return true;
9494

@@ -98,7 +98,7 @@ function isPrivateIp(ip) {
9898
const first = parseInt(parts[0], 10);
9999
const second = parseInt(parts[1], 10);
100100

101-
// Standard Private Ranges
101+
// Standard Private Ranges (10.x, 192.168.x, 172.16-31.x, 127.x)
102102
if (first === 127 || first === 10) return true;
103103
if (first === 192 && second === 168) return true;
104104
if (first === 172 && second >= 16 && second <= 31) return true;
@@ -107,87 +107,26 @@ function isPrivateIp(ip) {
107107
}
108108

109109
function getIPFromRequest(req) {
110-
// 1. Always check Cloudflare/Render verified header first
111-
if (req.headers['cf-connecting-ip']) {
112-
return req.headers['cf-connecting-ip'];
113-
}
114-
115-
const ipListHeader = req.headers['x-forwarded-for'];
116-
if (ipListHeader) {
117-
const IPs = ipListHeader.split(",").map((ip) => ip.trim());
118-
119-
// Start from the right (the most recent proxy)
120-
for (let i = IPs.length - 1; i >= 0; i--) {
121-
const curIp = IPs[i];
122-
// If we hit an IP that is NOT private, check if it's the last one left
123-
// or if it's a known public proxy (like Render's Azure IPs).
124-
if (!isPrivateIp(curIp)) {
125-
// If we are at the very first IP (index 0), it's definitely the user.
126-
if (i === 0) return curIp;
127-
128-
// If this isn't the first IP, it might be Render's public proxy.
129-
// We keep looping until we hit index 0.
130-
continue;
131-
}
132-
}
133-
// If the loop finished or we want the most likely candidate:
134-
return IPs[0];
135-
}
136-
137-
// Fallback to socket address, cleaning the prefix if it exists
138-
return (req.socket.remoteAddress || "").replace(/^::ffff:/, '');
139-
}function isPrivateIp(ip) {
140-
if (!ip) return false;
141-
142-
// Clean up Node's IPv6-mapped IPv4 prefix (::ffff:)
143-
const cleanIp = ip.replace(/^::ffff:/, '');
144-
145-
if (cleanIp === '::1' || cleanIp === 'localhost') return true;
146-
147-
const parts = cleanIp.split('.');
148-
if (parts.length !== 4) return false;
149-
150-
const first = parseInt(parts[0], 10);
151-
const second = parseInt(parts[1], 10);
152-
153-
// Standard Private Ranges
154-
if (first === 127 || first === 10) return true;
155-
if (first === 192 && second === 168) return true;
156-
if (first === 172 && second >= 16 && second <= 31) return true;
157-
158-
return false;
159-
}
160-
161-
function getIPFromRequest(req) {
162-
// 1. Always check Cloudflare/Render verified header first
163-
if (req.headers['cf-connecting-ip']) {
164-
return req.headers['cf-connecting-ip'];
165-
}
166-
167-
const ipListHeader = req.headers['x-forwarded-for'];
168-
if (ipListHeader) {
169-
const IPs = ipListHeader.split(",").map((ip) => ip.trim());
170-
171-
// Start from the right (the most recent proxy)
172-
for (let i = IPs.length - 1; i >= 0; i--) {
173-
const curIp = IPs[i];
174-
// If we hit an IP that is NOT private, check if it's the last one left
175-
// or if it's a known public proxy (like Render's Azure IPs).
176-
if (!isPrivateIp(curIp)) {
177-
// If we are at the very first IP (index 0), it's definitely the user.
178-
if (i === 0) return curIp;
179-
180-
// If this isn't the first IP, it might be Render's public proxy.
181-
// We keep looping until we hit index 0.
182-
continue;
183-
}
110+
// 1. Priority: The 'cf-connecting-ip' is provided by Render's edge.
111+
// This is the "gold standard" and is very hard to spoof.
112+
const cfIp = req.headers['cf-connecting-ip'];
113+
if (cfIp) return cfIp.trim();
114+
115+
// 2. Fallback: Parse the X-Forwarded-For list.
116+
const xff = req.headers['x-forwarded-for'];
117+
if (xff) {
118+
// We split the list into an array of IPs.
119+
const IPs = xff.split(',').map(ip => ip.trim());
120+
121+
// On Render, the user's real IP is ALWAYS the first one (index 0).
122+
// The others are Render/Azure/Cloudflare proxies.
123+
if (IPs.length > 0) {
124+
return IPs[0];
184125
}
185-
// If the loop finished or we want the most likely candidate:
186-
return IPs[0];
187126
}
188127

189-
// Fallback to socket address, cleaning the prefix if it exists
190-
return (req.socket.remoteAddress || "").replace(/^::ffff:/, '');
128+
// 3. Final Fallback: The direct connection IP (usually a proxy IP on Render)
129+
return (req.socket.remoteAddress || "").replace(/^::ffff:/, '').trim();
191130
}
192131

193132
var ipBanReasons = {

0 commit comments

Comments
 (0)