Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Cf Request Laravel Package

pdphilip/cf-request

cf-request is a Laravel package for accessing Cloudflare request data. It exposes headers like the connecting IP, visitor country, Ray ID, and other Cloudflare metadata, making edge information easy to use without manual header parsing.

View on GitHub
Deep Wiki
Context7

Getting Started

Requirements: Laravel 11, 12 or 13, PHP 8.3+ (the CI matrix now targets 8.3/8.4 only). If you are still on Laravel 10, stay on the 3.0.x line — v3.1.0 drops it.

composer require pdphilip/cf-request

The service provider is auto-discovered. Look at these three places first, in order:

  1. config/cf-request.php (publish with php artisan vendor:publish --tag=cf-request-config) — this is where the trust/verification behaviour is toggled. Read every key before going live; the defaults are sensible for a site that sits entirely behind Cloudflare, and wrong for a site that only partially does.
  2. The package's middleware / resolver binding — the day-to-day surface is a single call that gives you the real client IP rather than the Cloudflare edge IP.
  3. The bundled artisan commands — these are the Omniterm-powered interactive commands (see Gotchas for the ^3.0 bump).

First use case: you have an app behind the orange cloud, and $request->ip() is returning 172.70.x.x instead of your user's address, which breaks rate limiting, audit logs, and geo logic. Register the package's middleware (or call the resolver) in your bootstrap/app.php / HTTP kernel and confirm $request->ip() changes for a request arriving through Cloudflare. Do that in staging against a real proxied hostname — you cannot validate this from php artisan serve on localhost.

Implementation Patterns

Middleware-first, not controller-first. Add the package's middleware to your global or web stack so every downstream consumer ($request->ip(), RateLimiter, Auth::attempt logging, request()->ips()) sees the corrected address. Resolving the IP ad hoc inside a controller means every other middleware still sees the Cloudflare edge.

// bootstrap/app.php (Laravel 11+)
->withMiddleware(function (Middleware $middleware) {
    $middleware->append(\Pdphilip\CfRequest\...\Middleware::class);
})

Ordering matters when you have your own proxy layer. If the request path is user → Cloudflare → ALB/Nginx → app, the package must run after whatever rewrites REMOTE_ADDR, and your TrustProxies configuration must not clobber the header the package reads. Decide which layer is authoritative and disable the other — two systems both "fixing" the client IP is the most common source of a silently wrong address.

Wrap the IP in a value object at the boundary. Rather than sprinkling $request->ip() everywhere, resolve once in middleware and attach a ClientIp object to the request attributes. When the package's resolution strategy changes (or you add a second CDN), you have one place to update and your domain code stays clean.

Cache the Cloudflare range fetch. The package performs outbound HTTP calls to retrieve Cloudflare's published ranges. In production, ensure that fetch is cached (and warmed), otherwise you have an external dependency in the hot path of every request — or worse, in the path of your first request after a deploy. Put a long TTL behind it and add a fallback: if the range list cannot be fetched, fail closed on verification but still log loudly.

Testing pattern — fake the network, not the resolver. In tests/, use Http::fake() for the range endpoint and set the Cloudflare headers (CF-Connecting-IP, CF-Ray, X-Forwarded-For) manually on the test request. Assert both the happy path (trusted range → real IP surfaced) and the adversarial one (spoofed CF-Connecting-IP from an untrusted IP → header ignored). The second test is the one that protects you.

Version-pinned CI. Adopt the new scripts to match the maintainer's matrix:

composer test:l11
composer test:l12
composer test:l13
composer test:all

Use test:all in your own CI if you support multiple Laravel majors, and pin laravel/framework per job via a matrix rather than testing whatever resolves.

Queue and job context. The resolved IP lives on the request, so it is gone by the time a job runs. Snapshot it into the job payload (or into the audit record) at dispatch time — do not re-resolve later.

Gotchas and Tips

  • Laravel 10 is a hard break. ^3.1 will not install on L10. If you maintain a package or an app split across majors, constrain to ^3.0.1 || ^3.1 and let Composer pick, or you will be fighting resolution errors in CI.
  • PHP 8.3 is the new floor as far as the tested matrix goes. On 8.1/8.2 the package may still install (check composer.json require.php), but you are outside the supported/tested set — treat any bug there as yours to fix.
  • Omniterm jumped ^2 → ^3.0. This only bites you if you extend or publish the interactive artisan commands. If you have resources/views/vendor/ overrides or subclasses touching Omniterm APIs, re-test those commands after upgrading; a major bump there is not guaranteed to be signature-compatible.
  • The ->body() change is a fix, not a behaviour change — getBody()->getContents() and ->body() both return the response body string. But it is a signal: if you mock the HTTP client in tests with a hand-rolled ResponseInterface, your fake must support body(). Http::fake() handles this correctly; a manual Mockery double of PSR-7 may not.
  • Spoofing is the whole threat model. CF-Connecting-IP is just a header — anyone can send it. Verification against Cloudflare's ranges is what makes it trustworthy. Never enable the "just trust the header" mode on a hostname that is reachable directly (grey-clouded DNS record, origin IP leaked in DNS history, direct-to-IP scans). If the origin is reachable, the header is attacker-controlled.
  • Don't trust it behind a second CDN. Cloudflare → Fastly → app, or Cloudflare → Cloudflare-for-SaaS, will produce ranges the package does not know about. Verify which side is terminating and adjust TrustProxies accordingly.
  • Rate limiting gets subtly wrong before it gets obviously wrong. If your RateLimiter::for() keys on $request->ip() and the middleware is not applied to that route group, all your users share one bucket keyed on the edge IP. Add an assertion in tests that the key differs between two simulated clients.
  • Debugging tip: log $request->ips() (the full chain, not just the resolved one) plus REMOTE_ADDR for a day when rolling this out. The resolved IP alone hides why it resolved that way, and the chain makes proxy-ordering bugs obvious.
  • Larastan 3 users get a cleaner baseline from this release, but if you had a @phpstan-ignore or a baseline entry covering the old getBody()->getContents() call path, remove it or the baseline entry will start reporting as unused.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle