Skip to content

client_ip trusts the client-controlled leftmost X-Forwarded-For entry #7601

Closed

Description

@FarhanAliRaza

Problem

router.session.client_ip takes the leftmost entry of X-Forwarded-For (_build_static_router_data in reflex/app.py):

client_ip = headers.get("x-forwarded-for", client_ip).partition(",")[0].strip()

The client controls the leftmost entry. A proxy appends the address it saw to the right. So any client can set client_ip to any value by sending its own X-Forwarded-For. Reflex has no setting for which proxies to trust.

Raised in #7588 (comment).

Evidence

The client sends X-Forwarded-For: 6.6.6.6. The real client address is 203.0.113.7.

Proxy layout main #7588
nginx proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for (one line: 6.6.6.6, 203.0.113.7) 6.6.6.6 (spoofed) 6.6.6.6 (spoofed)
Proxy adds a separate line (6.6.6.6 / 203.0.113.7) 203.0.113.7 6.6.6.6 (spoofed)
Honest client, two proxies each add a line (203.0.113.7 / 10.0.0.2) 10.0.0.2 (wrong) 203.0.113.7

On main, the second row resists the spoof only because the last line wins. That same rule gives the wrong address in the third row. #7588 treats repeated lines as one list, as RFC 9110 §5.3 requires. Then all layouts behave like the first row, where main is already spoofable.

Possible fix

Take the client address from the right, and skip a configured number of trusted proxy hops (or a list of trusted proxy addresses). This is how Uvicorn's --forwarded-allow-ips and similar settings work. With no trusted proxy configured, use the socket peer address (asgi.scope["client"]), not the header.

This adds a config option, so it needs a decision on the API and on the default.

Activity

  1. linear-code commented on Oct 19, 2026

    @linear-code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions