SYMFONY/HTTP-FOUNDATION · GHSA-xg2q-4rrq-657q · Package
FIX → 8.1.11 · 7.4.23
Symfony's private_ranges trusted-proxies setting also matches IPv6 addresses that embed IPv4 (IPv4-mapped, 6to4, Teredo, NAT64), so any client sending those addresses is treated as a reverse proxy. An attacker can then spoof the client IP, host and scheme through X-Forwarded-* headers.
first public Nov 4, 2026 · 1 source
Could this affect your app?
Free first check from your website. Connect your repo for the full answer.
Applications on Symfony 7.1 to 7.4.22 or 8.0 to 8.1.10 that trust proxies with the private_ranges setting or pass IpUtils::PRIVATE_SUBNETS to Request::setTrustedProxies(). Exposure is greatest on servers listening on the IPv6 wildcard without IPV6_V6ONLY, or reachable over IPv6. Symfony 6.4 is not affected by default, and apps that use an explicit list of trusted proxies are not affected by this shortcut.
Not affected if default-config, explicit-trusted-proxies, symfony-6.4
Who cares ipv6, php, self-hosted, symfony
framework.trusted_proxies: private_ranges, which the deployment documentation recommends, and the private_ranges and PRIVATE_SUBNETS keywords of Request::setTrustedProxies() resolved to IpUtils::PRIVATE_SUBNETS. That constant is the deny list of NoPrivateNetworkHttpClient: it lists the private and special-purpose prefixes a server must not connect to, and since the fix for CVE-2026-48736 it also covers the IPv6 prefixes that embed an IPv4 address: IPv4-mapped (::ffff:0:0/96), IPv4-compatible (::/96), 6to4 (2002::/16), Teredo (2001::/32) and NAT64 (64:ff9b::/96). Over-blocking is safe for a deny list. Used as the allow list of trusted proxies, the same entries trust remote clients.
A client whose connection arrives with such a source address was treated as a reverse proxy, and the X-Forwarded-For, , , and headers it sends were honoured:
Check it with your coding agent
It reads this page and tells you if your code is affected.
Are you affected?
Ask from here. InfraStack checks the affected versions against your stack and answers in your inbox.
InfraStack checks the affected versions against your stack and answers in Slack, with the evidence and the fix.
composer.lock for symfony/http-foundation, symfony/framework-bundle or symfony/symfony.config/packages/framework.yaml for trusted_proxies: private_ranges or Request::setTrustedProxies calls using IpUtils::PRIVATE_SUBNETS or the private_ranges / PRIVATE_SUBNETS keywords.IPV6_V6ONLY, or is reachable over IPv6.Upgrade to 8.1.11 · 7.4.23
symfony/http-foundation and symfony/framework-bundle and symfony/symfony on 7.4 branch: 7.4.23symfony/http-foundation and symfony/framework-bundle and symfony/symfony on 8.x branch: 8.1.116.4.50 provides IpUtils::PRIVATE_RANGES; applications passing IpUtils::PRIVATE_SUBNETS to Request::setTrustedProxies() should switch to IpUtils::PRIVATE_RANGES.X-Forwarded-HostX-Forwarded-ProtoX-Forwarded-PortX-Forwarded-PrefixRequest::setTrustedProxies(['private_ranges'], Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PROTO);
$request = Request::create('/reset-password');
$request->server->set('REMOTE_ADDR', '::ffff:203.0.113.7');
$request->headers->set('X-Forwarded-For', '10.0.0.9');
$request->headers->set('X-Forwarded-Host', 'evil.example.com');
$request->headers->set('X-Forwarded-Proto', 'https');
$request->getClientIp(); // 10.0.0.9
$request->getHost(); // evil.example.com
$request->isSecure(); // true
Two kinds of deployments are exposed. A server listening on the IPv6 wildcard address without IPV6_V6ONLY reports every IPv4 client in the IPv4-mapped form, so every client is trusted. A server reachable over IPv6 trusts any client sourcing from 6to4 or Teredo space, which is globally routed, whatever its listener. A spoofed client address defeats IP allow lists, rate limiting and audit logs; a spoofed host poisons the URLs written into password reset and login link e-mails and into shared caches; a spoofed scheme satisfies requires_channel: https over plain HTTP.
The IPv4-mapped entry has been in the list since the private_ranges shortcut was introduced, in 7.1 for framework.trusted_proxies and in 7.2 for Request::setTrustedProxies(); the other prefixes joined it in 7.4.13 and 8.0.13.
Symfony 6.4 does not point any trusted proxy setting at IpUtils::PRIVATE_SUBNETS, so it is not affected by default. An application that passes that constant to Request::setTrustedProxies() itself is exposed the same way on any version. It should pass IpUtils::PRIVATE_RANGES instead, which 6.4.50 provides too.
Trusted proxies get their own list. IpUtils::PRIVATE_RANGES holds the ranges reserved for private networks, in which a reverse proxy can live: loopback, RFC 1918, shared address space (RFC 6598), link-local and unique local addresses. It contains no prefix a remote client can present as its own address, and private_ranges and PRIVATE_SUBNETS now resolve to it. IpUtils::PRIVATE_SUBNETS is unchanged and remains the deny list of NoPrivateNetworkHttpClient.
IpUtils::checkIp() now judges an IPv4-mapped address as the IPv4 host it embeds, so a proxy that a dual-stack listener reports as ::ffff:10.0.0.5 is still trusted through 10.0.0.0/8, and an IPv4 range configured explicitly in trusted_proxies or in access_control.ips matches the clients that reach a dual-stack listener as well.
Two behaviour changes for operators: the documentation, benchmarking, "this network" and reserved ranges the old list covered (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 198.18.0.0/15, 0.0.0.0/8, 240.0.0.0/4, 2001:db8::/32, 2001:2::/48) are no longer trusted by the shortcut and have to be listed explicitly when a proxy lives there; and applications that pass IpUtils::PRIVATE_SUBNETS to Request::setTrustedProxies() themselves should pass IpUtils::PRIVATE_RANGES instead.
The patches for this issue are available here for branch 7.4 (and forward-ported to 8.1 and 8.2). IpUtils::PRIVATE_RANGES is available here for branch 6.4.
We would like to thank Ivan Tse for reporting the issue and Nicolas Grekas for providing the fix.