> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://help.repliers.com/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# 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:

```typescript
// 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

```typescript
// 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

```typescript
// 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&#32;`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`&#32;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:

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