From 4c419bd1e44e556c9b1e41419ab67f51af982ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=BE=85=E0=BC=BB=20=C7=AC=C9=80=C4=A7=20=E0=BC=84?= =?UTF-8?q?=E0=BC=86=E0=BD=89?= Date: Sat, 30 May 2026 10:04:24 +0200 Subject: [PATCH 1/2] Document trusted proxies for IP detection in YOURLS Doc update to match new behavior upcoming patch --- docs/guide/advanced/proxy.md | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/guide/advanced/proxy.md b/docs/guide/advanced/proxy.md index 5ed401f..2d782bd 100644 --- a/docs/guide/advanced/proxy.md +++ b/docs/guide/advanced/proxy.md @@ -34,3 +34,42 @@ define( 'YOURLS_PROXY_BYPASS_HOSTS', 'example.com, *.mycorp.com' ); ``` This setting supports a comma separated list of hosts, using `*` as a wildcard if needed. + +## Trusted proxies for IP detection + +When YOURLS runs behind a reverse proxy, load balancer, or CDN (such as Cloudflare, nginx, or AWS ALB), the visitor's real IP address is passed along in the `X-Forwarded-For` HTTP header. By default, YOURLS only trusts `REMOTE_ADDR` and ignores forwarded headers, to prevent IP spoofing. + +If you need YOURLS to read the real client IP from forwarded headers, you must explicitly declare which proxy IPs are trusted, using the `get_ip_trusted_proxies` filter in a plugin. + +### Why this matters + +Without a trusted proxy list: + +- `REMOTE_ADDR` is used, which is the IP of your proxy, not the visitor's. +- Click statistics and geographic data will reflect your proxy's IP, not actual visitors. +- Flood protection will treat all visitors as the same IP. + +With a trusted proxy list: + +- YOURLS checks if `REMOTE_ADDR` matches a trusted proxy. +- If it does, it reads the visitor's real IP from `X-Forwarded-For`. +- If it doesn't, `REMOTE_ADDR` is used as-is, and forwarded headers are ignored. This prevents untrusted clients from spoofing their IP. + +### Configuring trusted proxies via a plugin + +Use the `get_ip_trusted_proxies` filter to return an array of trusted proxy IPs (individual IP or IP range). Create a plugin with the following: + +```php +yourls_add_filter( 'get_ip_trusted_proxies', 'my_trusted_proxies' ); + +/** + * Define trusted proxy IPs for X-Forwarded-For header resolution. + * @return array List of trusted proxy IPs + */ +function my_trusted_proxies() { + return [ + '10.0.0.1', // local nginx reverse proxy + '198.41.128.0/17', // Cloudflare IPv4 range (example - see https://www.cloudflare.com/ips/) + ]; +} +``` From f63a9e74b9d3a8e6b057a9cb5901f993e1b7c824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=BE=85=E0=BC=BB=20=C7=AC=C9=80=C4=A7=20=E0=BC=84?= =?UTF-8?q?=E0=BC=86=E0=BD=89?= Date: Sat, 6 Jun 2026 11:18:31 +0200 Subject: [PATCH 2/2] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Léo Colombaro --- docs/guide/advanced/proxy.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/guide/advanced/proxy.md b/docs/guide/advanced/proxy.md index 2d782bd..af61e39 100644 --- a/docs/guide/advanced/proxy.md +++ b/docs/guide/advanced/proxy.md @@ -37,9 +37,10 @@ This setting supports a comma separated list of hosts, using `*` as a wildcard i ## Trusted proxies for IP detection -When YOURLS runs behind a reverse proxy, load balancer, or CDN (such as Cloudflare, nginx, or AWS ALB), the visitor's real IP address is passed along in the `X-Forwarded-For` HTTP header. By default, YOURLS only trusts `REMOTE_ADDR` and ignores forwarded headers, to prevent IP spoofing. +When YOURLS runs behind a reverse proxy, a load balancer, or a CDN, the visitor's real IP address is passed along in the `X-Forwarded-For` HTTP header. +By default, YOURLS only trusts `REMOTE_ADDR` and ignores forwarded headers, to prevent IP spoofing. -If you need YOURLS to read the real client IP from forwarded headers, you must explicitly declare which proxy IPs are trusted, using the `get_ip_trusted_proxies` filter in a plugin. +If YOURLS needs to read the real client IP from forwarded headers, the trusted proxy IPs must be declared, using the `get_ip_trusted_proxies` filter in a plugin. ### Why this matters