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

Wn Cloudflare Plugin Laravel Package

winter/wn-cloudflare-plugin

Winter CMS plugin that integrates Cloudflare with your site, providing cache purging, proxy and DNS management, and related Cloudflare settings from the Winter backend. Simplifies keeping cached content fresh and managing Cloudflare features alongside your CMS configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require winter/wn-cloudflare-plugin
php artisan winter:up

Then enable Cloudflare under Settings → System → Updates & Plugins (or add Winter\Cloudflare\Plugin to config/plugins.php if you commit plugin state). There are no migrations, no settings table, and no required configuration — this is a "install it and the proxy just works" plugin, so the whole integration surface is small.

Look here first, in this order:

  1. plugins/winter/cloudflare/Plugin.php — read register()/boot() in full; the entire behaviour lives there.
  2. Any middleware/ or classes/ directory shipped with it — this is where the trusted-proxy / real-IP logic sits.
  3. The README's "requirements" section — it will tell you which Cloudflare SSL mode and headers it assumes.

Your first use case: you put a Winter site behind Cloudflare (orange-cloud), and one of three things breaks:

  • Generated asset/form URLs come out http:// → mixed content warnings and failed POSTs.
  • An infinite redirect loop between Cloudflare and the origin.
  • backend_users.last_login_ip, system_event_log, or a login throttle shows Cloudflare edge IPs (172.68.x.x, 104.16.x.x) instead of real visitors — meaning "too many login attempts" lockouts hit everyone at once.

After installing, verify concretely rather than trusting it:

// In a temporary route or tinker-like context
dd(request()->ip(), request()->getScheme(), request()->header('CF-Connecting-IP'));

getScheme() should be https and ip() should be the visitor's address, not a Cloudflare range. If both are correct, you are done — this plugin is not something you interact with day to day.

Implementation Patterns

Treat it as infrastructure, not application code. The correct pattern is: install, verify the three signals above, then never touch it again. Resist the urge to import its classes into your own controllers.

Environment gating. Cloudflare is only in the path in production/staging. Locally (DDEV, Valet, Sail) there is no CF-Connecting-IP header. Make sure the plugin degrades safely and that you don't hardcode https forcing in local config:

// config/app.php or a project plugin — only force the scheme outside local
if (!App::environment('local')) {
    URL::forceScheme('https');
}

Middleware ordering matters more than the plugin itself. The real-IP resolution must run before the session, auth, and throttle middleware, otherwise rate limiting and audit logs capture edge IPs. If you have a custom plugin with its own boot()-registered middleware, check ordering — in Winter, middleware registered via $this->app['Illuminate\Contracts\Http\Kernel'] push order determines this. If the plugin's middleware is appended, you may need to push your own TrustProxies earlier instead.

Layered proxies. Cloudflare in front of an origin load balancer or a Cloudflare Tunnel means there are two hops. Trusting only Cloudflare ranges will produce the LB's IP. Extend the trusted set for the additional hop, keeping Cloudflare's ranges intact.

Cloudflare Tunnel / Argo users. The visitor connection terminates at the tunnel daemon; the CF-Connecting-IP header is your source of truth and REMOTE_ADDR is 127.0.0.1. Verify the plugin reads the header rather than only parsing X-Forwarded-For.

Pairing with caching. This plugin does not purge Cloudflare's cache on content changes. When you publish a page and still see the old version, that's Cloudflare's edge cache, not Winter's. Handle it with Cloudflare Cache Rules (bypass on /admin*, /index.php/back*, and anything session-bearing), and use the Cloudflare API from a queue job or a cms.page.updated event listener if you need programmatic purges.

Custom code should use the framework accessors, never superglobals:

$ip     = request()->ip();                 // real client IP after the plugin runs
$scheme = request()->getScheme();          // 'https'
$secure = request()->isSecure();

Access-logging / analytics plugins in the same project will benefit automatically — install this before debugging any "wrong IP in the log" report.

Deployment: nothing to migrate, nothing to seed. It is safe to add to a composer.json you deploy via CI. Just remember php artisan cache:clear (and winter:clear-compiled) so the newly registered middleware is picked up — a stale compiled container is the #1 cause of "I installed it and nothing changed."

Gotchas and Tips

  • Never trust all proxies (*) as a shortcut. If the origin is reachable directly (by IP, or via a misconfigured DNS record that isn't proxied), an attacker can send a forged X-Forwarded-For and spoof their IP — which defeats login throttling and poisons your audit trail. Restrict the origin at the firewall/security-group level to Cloudflare's published ranges so the header can only come from Cloudflare.

  • Cloudflare's IP list changes. Any hardcoded range list in the plugin will eventually go stale, and the symptom is subtle: real IPs suddenly show as REMOTE_ADDR again. Cross-check against https://www.cloudflare.com/ips-v4 and ips-v6 after Cloudflare announces changes; if the plugin hardcodes, consider contributing a config override rather than forking.

  • Redirect loop = SSL mode. If Cloudflare is set to Flexible, Cloudflare talks plain HTTP to the origin while telling the browser it's HTTPS, and a forced-HTTPS redirect at the origin loops forever. Fix at the edge: Full (Strict). No plugin can save you from Flexible.

  • "Pseudo IPv4" rewrites CF-Connecting-IP. If enabled in the Cloudflare dashboard, IPv6 visitors get a synthetic 240.0.0.0/4 address in that header. Turn it to Off (or Add Header, which preserves the original in Cf-Pseudo-IPv4) if you need accurate IPv6 logging.

  • Backend CSRF / session weirdness. Cookie domain and SESSION_DOMAIN/APP_URL mismatches cause silent POST failures on /admin. If logins fail only in production, check those env values before blaming the plugin.

  • Mixed content is not always scheme detection. Winter's asset combiner and Url::asset() output can be cached in the compiled/asset cache from a pre-Cloudflare era. Clear caches after enabling.

  • Debugging recipe, in this order: enable access logging or a debug route, then compare $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_X_FORWARDED_FOR'], $_SERVER['HTTP_CF_CONNECTING_IP'], $_SERVER['HTTP_X_FORWARDED_PROTO'] against request()->ip() and request()->getScheme(). That single dump tells you whether the plugin is loading, whether the headers arrive, and which hop is being resolved. Test locally with curl -H 'X-Forwarded-For: 203.0.113.9' -H 'X-Forwarded-Proto: https' https://your-site.test/ip-check.

  • Extension points are thin. This is a ~single-class plugin with essentially no events. The realistic customisation is: (a) override the trusted-proxy list via config if supported, (b) register your own TrustProxies middleware in a project plugin and disable this one, or (c) fork. There is no documented public API to build on — treat it as a shim.

  • Maintenance risk is real. 4 stars, 0 dependents, and a very small codebase. Read the source once (it's short) so that if it stops being maintained you can inline the equivalent middleware into your project plugin in an afternoon. The functionality is worth understanding regardless: Request::setTrustedProxies(), honouring X-Forwarded-Proto, and URL::forceScheme('https') cover 90% of it.

  • Only add it when the origin actually needs it. If your Winter site already returns correct IPs and schemes (because nginx has real_ip configured with ngx_http_realip_module), this plugin adds a second, potentially conflicting, source of truth. Pick one layer and let it own the problem.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core