Articles on: Data Security

LLM Guidance For Configuring The Repliers API Firewall

Repliers API IP Firewall: Forwarding the Real Client IP


What this solves


The Repliers API supports an IP-based firewall. When you proxy requests through a backend server, the Repliers API sees your server's IP — not your end users'. To enforce per-client IP rules, you must extract the real client IP and forward it in the x-repliers-forwarded-for header on every outbound Repliers API request.



How it works


Your backend sits between the browser and Repliers. The browser's IP arrives via the X-Forwarded-For header (populated by your load balancer / CDN / reverse proxy). Your server reads that chain, finds the first public (non-private) IP, and forwards it to Repliers.


Browser → CDN/Load Balancer → Your API Server → Repliers API
(real IP) (appends to XFF) (reads XFF) (enforces firewall)



Implementation (TypeScript / Express)


1. Trust the proxy chain


In your Express app setup, tell Express to trust the upstream proxy so X-Forwarded-For is populated correctly:


// app.ts
app.set("trust proxy", true);


Warning: Only set this if your server is actually behind a trusted proxy (load balancer, CDN, Render, Railway, Fly.io, etc.). Do not set it if the server is directly internet-facing — a client could spoof the header.



2. Create the IP extraction utility


// lib/repliers-headers.ts
import type { Request } from "express";

const IPV4 = /^(\d{1,3}\.){3}\d{1,3}$/;
const IPV6 = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;

function isValidIp(ip: string): boolean {
return IPV4.test(ip) || IPV6.test(ip);
}

function isPrivateIp(ip: string): boolean {
return (
/^10\./.test(ip) ||
/^172\.(1[6-9]|2\d|3[01])\./.test(ip) ||
/^192\.168\./.test(ip) ||
ip === "127.0.0.1" ||
ip === "::1"
);
}

export function getRepliersHeaders(req: Request): Record<string, string> {
// X-Forwarded-For is a comma-separated chain: client, proxy1, proxy2, ..., edge
const forwardedFor = (req.headers["x-forwarded-for"] as string | undefined ?? "")
.split(",")
.map((ip) => ip.trim())
.filter(Boolean);

// Take the first public IP — private IPs are always internal proxy hops.
const realIp =
forwardedFor.find((ip) => isValidIp(ip) && !isPrivateIp(ip)) ??
forwardedFor[0] ??
req.socket.remoteAddress?.replace(/^::ffff:/, "") ??
"";

return { "x-repliers-forwarded-for": realIp };
}


IP selection priority:

  1. First valid, public IP in the X-Forwarded-For chain
  2. First IP in the chain (fallback when all are private — unusual, but safe)
  3. req.socket.remoteAddress (direct connection, no proxy)
  4. Empty string (should never happen in practice)


Private ranges filtered out: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.1, ::1



3. Spread the headers into every Repliers API call


// routes/listings.ts
import { getRepliersHeaders } from "../lib/repliers-headers";

router.get("/listings", async (req, res) => {
const apiKey = process.env["REPLIERS_API_KEY"];

const response = await fetch("https://api.repliers.io/listings", {
headers: {
"repliers-api-key": apiKey,
"Content-Type": "application/json",
...getRepliersHeaders(req), // ← always spread this
},
});

// ...
});


Apply ...getRepliersHeaders(req) to every route that calls the Repliers API — listings, listing detail, locations, statistics, etc.



Gotchas


CDN/proxy strips or rewrites X-Forwarded-For

Some platforms (Cloudflare, Render) use their own header variants (CF-Connecting-IP, X-Real-IP). If X-Forwarded-For is empty or always shows the edge IP, check your platform's docs and adjust the extraction logic accordingly.


Multiple proxies append IPs left-to-right

The format is client, proxy1, proxy2. The leftmost entry is the original client. The rightmost entries are internal/edge hops. This utility correctly picks the leftmost public IP.


IPv6-mapped IPv4 addresses

Node.js sometimes returns ::ffff:1.2.3.4 from req.socket.remoteAddress. The .replace(/^::ffff:/, "") strip handles this.


trust proxy and spoofing

With trust proxy: true, Express trusts the entire X-Forwarded-For chain. If an attacker sends a crafted X-Forwarded-For header and your CDN/load balancer appends to it (rather than replacing it), the real IP is still at the right end of the chain — but this utility picks the leftmost public IP, which could be spoofed. For high-security environments, use trust proxy: N (a count) to trust only the N rightmost IPs, or validate against your known edge IP ranges.



Testing locally


When running locally without a proxy, X-Forwarded-For is absent and req.socket.remoteAddress is 127.0.0.1 or ::1. The header sent to Repliers will be an empty string or a loopback address — this is fine for local development, as the firewall rules apply in production.


To simulate a real IP in local dev:


curl -H "X-Forwarded-For: 203.0.113.42" http://localhost:3000/api/listings?locationId=...


Updated on: 07/05/2026

Was this article helpful?

Share your feedback

Cancel

Thank you!