diff --git a/.env.example b/.env.example index b9b75b2..bd6cd72 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,10 @@ APP_DEBUG=true APP_URL=http://127.0.0.1:8000 APP_TIMEZONE=Asia/Singapore +# Behind Cloudflare / Nginx reverse proxy: trust X-Forwarded-* for real client IP. +# Use * when origin is only reachable via the proxy; otherwise list proxy CIDRs. +TRUSTED_PROXIES=* + APP_LOCALE=en APP_FALLBACK_LOCALE=en APP_FAKER_LOCALE=en_US diff --git a/bootstrap/app.php b/bootstrap/app.php index 6aae66b..139d34c 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -23,6 +23,18 @@ return Application::configure(basePath: dirname(__DIR__)) health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { + // Cloudflare / reverse proxy: honor X-Forwarded-* so $request->ip() + // is the visitor IP. TRUSTED_PROXIES=* | CIDR list | empty to disable. + $trustedProxies = env('TRUSTED_PROXIES', '*'); + if ($trustedProxies === '*') { + $middleware->trustProxies(at: '*'); + } elseif (is_string($trustedProxies) && trim($trustedProxies) !== '') { + $middleware->trustProxies(at: array_values(array_filter(array_map( + 'trim', + explode(',', $trustedProxies) + )))); + } + $middleware->alias([ 'admin.super' => \App\Http\Middleware\EnsureSuperAdmin::class, 'panel.host' => \App\Http\Middleware\EnsurePanelHost::class, diff --git a/tests/Feature/PageVisitTest.php b/tests/Feature/PageVisitTest.php index ede4d41..070e3ba 100644 --- a/tests/Feature/PageVisitTest.php +++ b/tests/Feature/PageVisitTest.php @@ -42,6 +42,24 @@ class PageVisitTest extends TestCase $this->assertSame($ua, $row->user_agent); } + #[Test] + public function hit_uses_x_forwarded_for_when_proxies_trusted(): void + { + Cache::flush(); + + $this->call('GET', '/statistic/t', [ + 'c' => self::CHANNEL, + 'u' => '11111111-2222-4333-8444-555555555555', + ], [], [], [ + 'REMOTE_ADDR' => '104.16.1.1', + 'HTTP_X_FORWARDED_FOR' => '203.0.113.50', + ])->assertOk(); + + $row = PageVisit::query()->first(); + $this->assertNotNull($row); + $this->assertSame('203.0.113.50', $row->ip); + } + #[Test] public function hit_debounces_duplicate_uid(): void {