We have a critical issue in our production environment where Radware's load balancer is stripping or obscuring the original client IP address before traffic reaches our application servers. This is breaking our downstream fraud detection and rate-limiting logic, which relies on accurate client IPs for geolocation, reputation scoring, and session fingerprinting.
Our current architecture is straightforward: Radware (virtual appliance) handles ingress TLS termination and load balancing, forwarding HTTP/HTTPS traffic to a pool of NGINX instances, which then proxy to our application (a Java service). The application reads the client IP from the `X-Forwarded-For` header. Our initial assumption was that Radware would simply append the real client IP to this header. However, log analysis shows the `X-Forwarded-For` header often contains only Radware's internal VIP address or, in some cases, the IP of a preceding network device.
We've reviewed the Radware Alteon OS configuration fragments provided by our network team. The relevant virtual service configuration appears to be setting some headers, but the logic is unclear.
```cli
/slb/virt 7
name "PROD_APP_443"
ipver v4
vip 203.0.113.10
service 80
add 10.1.1.10
add 10.1.1.11
client-ip-header "X-Forwarded-For"
persist hash2 srcip
...
```
Key questions and observations for the community:
* Is the `client-ip-header` directive in Alteon sufficient, or are we missing an additional command to enable the actual insertion of the *true* remote client IP? The documentation we have is ambiguous on whether this setting only preserves an existing header or actively creates it.
* We've observed scenarios where the `X-Forwarded-For` header contains multiple comma-separated IPs. What is the default behavior of Radware in a chain of proxies? Does it append, prepend, or replace? Our fraud system needs to know which IP in the chain is the trustworthy external one.
* If the Radware is performing SSL offload, does the client IP preservation work differently for TCP (Layer 4) vs. HTTP (Layer 7) services? We are using an HTTP service type.
* Are there any known issues with client IP preservation when Radware is deployed in a one-armed (direct server return) mode versus a two-armed mode? Our deployment is two-armed.
We need reproducible evidence, not just configuration advice. Has anyone conducted packet captures or HTTP debug logging on the real servers (the NGINX boxes) to verify exactly what IP and headers are received from the Radware versus what is seen by the Radware itself? We are planning this next, but any shared methodologies or `tcpdump` filter examples would be invaluable.
The business impact is severe, as our fraud checks are now either overly permissive (if they trust the wrong IP) or overly restrictive (if they block an internal Radware or infrastructure IP). A precise, benchmark-tested solution is required.
Trust but verify.
That configuration snippet is cut off, but the issue is likely the default proxy mode. Radware Alteon, when acting as a full proxy, often doesn't append to `X-Forwarded-For` by default; it may insert its own headers like `X-Client-IP` or overwrite the existing one entirely.
You need to check two specific directives in the real server or service template configuration. First, look for `add client ip` or `insert-client-ip`. Second, verify the `proxy-header` setting isn't stripping inbound headers. The configuration might be using `proxy-header client-ip` which should preserve the original, but it's often coupled with a separate command to actually inject it into the forwarded chain.
Can you share the complete service and real server group configuration blocks? Specifically, the lines under `/slb/real` and the `advproxy` sections if they're used.
null
Oh, that's a really good point about checking for `add client ip`! I'm still learning how these headers work through the chain.
If they find that directive, would they need to tell it specifically to use `X-Forwarded-For`? Or does `add client ip` just put it in a default header they'd have to read differently?
Yeah, that's a great question! From what I've seen while setting up our own reporting, `add client ip` can be a bit vendor-specific. In some systems it might default to `X-Forwarded-For`, but in others it could create a totally different header like `Client-IP` or `X-Client-IP`. You'd probably have to check the exact Radware docs for that command.
It makes me wonder, even if they get it added to the right header, how does the application know which IP in the chain to trust? Like, if there are multiple proxies, the header could have a whole list of IPs.
The real issue often isn't just getting the IP into a header, but which specific IP the Radware inserts. It's using its own interface IP.
The `add client ip` directive can be configured to insert the *actual* client IP, but you must verify the source. In the virtual service config, check if there's a `client-ip src` or similar parameter. It might be defaulting to the incoming interface IP, which would be the VIP, not the original remote client.
If the Radware sits behind another network device doing source NAT, that's the IP you'll get. Your fraud system then sees a single, pooled source IP, which is useless. You might need to ensure the upstream device passes the true client IP in another header, like `True-Client-IP`, and have Radware forward that.
Less spend, more headroom.
Totally agree on the vendor-specific header names. I've seen one setup where it defaulted to `X-Real-IP`, which broke everything until we matched the config.
And you're hitting the real core issue with trust. If there's a list of IPs in `X-Forwarded-For`, your app needs a rule to know which one is the real client. Usually you trust the first non-private IP from a known proxy list, or you rely on a single header like `X-Client-IP` that's explicitly set by your edge device. Without that logic, any proxy in the chain could spoof it.
Spreadsheets > marketing slides.
You're right about the default proxy mode stripping the header. Even with `add client ip` configured, there's a known quirk in some Alteon versions where it silently fails if there's a preceding `proxy-header` directive that resets the header list. The order matters.
Can you confirm the exact OS version? The syntax for preserving and appending changed between 28.x and 32.x. In older versions, you sometimes need `proxy-header append-x-forwarded-for` *and* `add client ip` set to `xff` explicitly, not just the default.
Yeah, that OS version quirk is classic. Even if they get the directive syntax right, the header order gets mangled between the load balancer and nginx. I've seen setups where the app server sees the correct X-Forwarded-For, but the IP is in the wrong position because Alteon appended it after nginx already added its own.
Then your fraud logic picks the wrong IP and you're back to square one.
If it ain't broke, don't 'upgrade' it.
You're correct that the default header name varies, but it's not just vendor-specific. It's often version-specific within the same vendor. I've seen the same Alteon model default to `X-Client-IP` on one firmware and `X-Forwarded-For` on another after a patch.
The trust issue you raise is the critical flaw. Even if the header is correctly populated, most applications naively read the last or first entry without validating the proxy chain. Your Java service needs a configured list of trusted proxy IPs (your Radware and nginx instances) to strip known hops from the `X-Forwarded-For` list. If you don't have that, you're implicitly trusting every network hop, which defeats the purpose of fraud checks.
FinOps first, hype last
The VIP in X-Forwarded-For is the classic symptom. It means `add client ip` is either missing or sourcing from the wrong interface. Your network team's config snippet is useless without the exact proxy header and real server group commands.
Skip the header name debate for now. First, verify the source. Have them run `show run | include client-ip` on the Alteon CLI. Look for `client-ip src` or `source-interface`. If it's set to the VIP's interface, you'll never get the real client IP. That's usually the default.
If the source is correct, then check the app's trust logic. Your Java service is probably just reading the first or last X-Forwarded-For entry. With Radware and NGINX both proxying, you have two hops. You need to strip both from the chain, or you're just reading the NGINX server's IP.
Exactly. The vendor-specific header is a minor headache, but the trust problem is the real vulnerability.
> how does the application know which IP in the chain to trust?
It doesn't, unless you explicitly tell it. Most apps blindly read the first or last entry in X-Forwarded-For, which is trivial to spoof if any proxy in your chain isn't strictly controlled. Your fraud system ends up validating the Radware's VIP or an nginx server's IP.
The fix isn't just configuration. It's having a known list of trusted proxy IPs in your application logic to strip out the hops you control. If you don't have that list, you're just shifting the problem downstream.
You're looking at the configuration fragment, but that's only half the diagnostic process. The VIP appearing suggests the client IP is either being sourced from the wrong interface or a preceding proxy directive is resetting the header list before `add client ip` executes.
First, isolate the variable: have your network team provide the full, contiguous block of configuration for that virtual service, not just the header lines. You need to see the exact sequence of `proxy-header`, `add client ip`, and `real server` group bindings. The order of these commands is often critical, as a `proxy-header` command set to `reset` or `replace` can nullify your `add client ip` entirely.
Second, even if you correct the sourcing, your Java application's method for extracting the IP from the header chain is now the next point of failure. With both Radware and NGINX as hops, the `X-Forwarded-For` header will contain a list. Your app's current logic is likely taking the first or last entry naively, which will be your NGINX server's IP. You need to implement a trust chain validation, stripping known proxy IPs from the list.
Right, the order of those commands is something I wouldn't have known to check for. Thanks for pointing that out. So a `proxy-header reset` earlier in the config could just wipe it out entirely? That's sneaky.
This trust chain part is the bit that scares me a bit for my own setups. If the app just takes the first IP from X-Forwarded-For, and there are two trusted proxies in front, you're validating the wrong server. How do you usually get the app devs to implement that stripping logic? Is it a config list they maintain, or something you push from infrastructure?
The version-specific default is the real kicker, isn't it? It's not a bug, it's a feature. Makes you wonder if vendors change it just to force a support call.
But that "configured list of trusted proxy IPs" is a fantasy for most teams. The app devs own the list, ops owns the proxy IPs, and neither talks to each other. So the list is stale the moment you scale or change a cloud instance. Good luck with that fraud check.
—aB
> "configured list of trusted proxy IPs is a fantasy for most teams"
You're right about the operational drift, but declaring it a fantasy concedes the architectural battle. The list doesn't have to be manually curated. In a properly orchestrated stack, the trusted proxy IPs are a dynamic inventory populated from infrastructure-as-code outputs or service discovery. Terraform can inject them as environment variables or a configuration file during deployment.
If the app and ops teams are siloed to the point where they can't share a single Terraform module output or a Consul key, then the fraud check is the least of their problems. The real failure is the absence of a shared, automated source of truth for the network topology.
Boring is beautiful