solution-forest/cloudflare-tunnel

Generic Composer bin wrapping cloudflared for local HTTPS tunnels (any local HTTP server; optional Laravel notes in README).

Maintainers

Package info

github.com/solutionforest/cloudflare-tunnel

Language:Shell

pkg:composer/solution-forest/cloudflare-tunnel

Transparency log

Statistics

Installs: 9

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-08-01 08:28 UTC

This package is auto-updated.

Last update: 2026-08-01 08:38:28 UTC


README

License: MIT PHP GitHub

Composer bin that wraps cloudflared so any local HTTP server gets a public HTTPS URL in one command.

vendor/bin/cloudflare-tunnel

Contents

Features

  • Composer bin entry pointvendor/bin/cloudflare-tunnel after a require-dev install
  • Quick tunnels by default — zero-setup https://….trycloudflare.com via Cloudflare’s trycloudflare flow
  • Named tunnels — set TUNNEL_NAME (or CLOUDFLARE_TUNNEL_NAME) to run a tunnel from ~/.cloudflared/config.yml (or CLOUDFLARED_CONFIG)
  • Local URL resolution — env → CLI → auto-detect → default :8000, with a printed reason for the choice
  • Auto-detect — reads local APP_URL from ./.env when it is http://127.0.0.1:PORT / localhost:PORT, else probes listening ports 8000, 8080, 3000, 80 (lsof or nc)
  • Extra cloudflared args — optional CLOUDFLARED_EXTRA_ARGS (space-separated)
  • Foreground helper UX — streams cloudflared output, highlights the public URL when found, and reminds you to set APP_URL
  • Framework-agnostic — Laravel, Node, static files, or anything listening on HTTP (not a Laravel package; no service provider)

Requirements

  • cloudflared on PATH
  • Bash 3.2+ (macOS system Bash is fine)
  • Composer (to install the package and expose vendor/bin/cloudflare-tunnel)
  • PHP ≥ 8.0 (Composer package constraint only; the binary is Bash)

Installation

1. Require the package (Packagist)

composer require --dev solution-forest/cloudflare-tunnel:^1.0

Or in composer.json:

{
  "require-dev": {
    "solution-forest/cloudflare-tunnel": "^1.0"
  }
}
composer update solution-forest/cloudflare-tunnel

No repositories entry is required — the package is on Packagist.

Alternatives (path / VCS)

Path (local package development):

{
  "repositories": [
    {
      "type": "path",
      "url": "packages/cloudflare-tunnel",
      "options": { "symlink": true }
    }
  ],
  "require-dev": {
    "solution-forest/cloudflare-tunnel": "*"
  }
}

VCS (without Packagist):

{
  "repositories": [
    {
      "type": "vcs",
      "url": "https://github.com/solutionforest/cloudflare-tunnel"
    }
  ],
  "require-dev": {
    "solution-forest/cloudflare-tunnel": "^1.0"
  }
}

2. Install cloudflared

brew install cloudflare/cloudflare/cloudflared

Other platforms: Cloudflare downloads.

3. Run

Start your local app, then:

vendor/bin/cloudflare-tunnel

Optional project script (this package does not ship Composer scripts; add them in the consuming app if you want):

{
  "scripts": {
    "tunnel": [
      "Composer\\Config::disableProcessTimeout",
      "vendor/bin/cloudflare-tunnel"
    ]
  }
}
composer run tunnel

When the public URL appears, set your app’s public base URL to that HTTPS origin (e.g. APP_URL), restart the local server, and clear any config cache.

Configuration

CLI flags

Flag Description
--url=URL Local origin to expose (e.g. http://127.0.0.1:9000)
--port=PORT Local port; host from HOST or 127.0.0.1
-h, --help Show help
vendor/bin/cloudflare-tunnel
vendor/bin/cloudflare-tunnel --port=9000
vendor/bin/cloudflare-tunnel --url=http://127.0.0.1:9000

Environment variables

Variable Default Purpose
TUNNEL_LOCAL_URL (auto / default) Full local origin (highest priority)
HOST / PORT 127.0.0.1 / (unset) Used when TUNNEL_LOCAL_URL / --url unset; PORT must be set to take this path
TUNNEL_NAME (empty) Named tunnel; alias CLOUDFLARE_TUNNEL_NAME
CLOUDFLARED_CONFIG ~/.cloudflared/config.yml Named-tunnel config path
CLOUDFLARED_EXTRA_ARGS (empty) Extra args passed to cloudflared (space-separated)
TUNNEL_LOCAL_URL=http://127.0.0.1:8000 vendor/bin/cloudflare-tunnel
TUNNEL_NAME=my-app vendor/bin/cloudflare-tunnel

Local URL resolution (first match wins)

The script prints which URL was chosen and why.

Priority Source Notes
1 TUNNEL_LOCAL_URL Full URL
2 --url= CLI full URL
3 PORT / --port= Host from HOST or 127.0.0.1
4 Auto-detect Heuristic below
5 Default http://127.0.0.1:8000

Auto-detect:

  1. If ./.env has APP_URL=http://127.0.0.1:PORT or http://localhost:PORT (explicit port), use that host/port.
  2. Else probe listening ports via lsof (or nc): 8000, 8080, 3000, 80.
  3. Otherwise fall back to http://127.0.0.1:8000.

Port 5173 alone is treated as Vite, not the app — auto-detect does not prefer it as the tunnel target.

Quick vs named tunnels

Mode How Notes
Quick (default) No TUNNEL_NAME Random https://….trycloudflare.com; fine for short-lived OAuth/webhook labs
Named TUNNEL_NAME=… Requires config at CLOUDFLARED_CONFIG; DNS/ingress must match your public hostname and local target

Testing

This package ships no automated test suite and no dry-run / mock mode for cloudflared. Verify changes with the checks below.

Syntax check

From the package root:

bash -n bin/cloudflare-tunnel

Exit 0 means the script parses under Bash.

Help / missing binary smoke

bin/cloudflare-tunnel --help
# With cloudflared temporarily off PATH, expect a clear install error:
PATH=/usr/bin:/bin bin/cloudflare-tunnel

Manual smoke (end-to-end)

  1. Start a local HTTP server on a known port (e.g. php -S 127.0.0.1:8000 or php artisan serve).
  2. In another terminal: vendor/bin/cloudflare-tunnel (or bin/cloudflare-tunnel --port=8000 from the package tree).
  3. Wait for a https://….trycloudflare.com URL in the output.
  4. Open that URL in a browser and confirm it reaches the local app.
  5. Stop with Ctrl+C.

Optional Composer scripts

The package composer.json defines no scripts. Consuming projects may add a tunnel script (see Installation) and run composer run tunnel as a convenience wrapper — that is project-local, not part of this package’s release verification.

Why

Localhost is enough for most day-to-day work. It is not enough when something on the public internet must call you back:

  • OAuth providers that require an https:// redirect URI
  • Webhooks from Stripe, GitHub, Slack, and similar services
  • Sharing a WIP page with a teammate or testing on a phone

Alternatives exist. Herd Expose (herd share) is polished for Laravel Herd + Vite. ngrok is widely known and feature-rich. This package is a thin, Composer-installable wrapper around Cloudflare’s free quick tunnel (and optional named tunnels) — useful when you already have (or prefer) cloudflared, want a vendor/bin entry point, and do not need Herd or an ngrok account for the job.

It does not replace those tools; it is another path with the same job: local app → public HTTPS.

How it works

flowchart LR
  subgraph local [Your machine]
    App["Local HTTP app<br/>e.g. :8000 / :3000"]
    Bin["vendor/bin/cloudflare-tunnel"]
    Cf["cloudflared"]
  end

  subgraph edge [Cloudflare]
    Quick["trycloudflare.com<br/>quick tunnel"]
    Named["Named hostname<br/>TUNNEL_NAME + config.yml"]
  end

  Browser["Browser / OAuth / webhook"]

  App -->|"HTTP to local URL"| Cf
  Bin -->|"resolves local URL<br/>then starts"| Cf
  Cf -->|"outbound tunnel"| Quick
  Cf -->|"outbound tunnel"| Named
  Quick -->|"HTTPS"| Browser
  Named -->|"HTTPS"| Browser
Loading
  1. The script resolves a local origin (env, CLI flags, auto-detect, or default :8000).
  2. With no TUNNEL_NAME, it runs a quick tunnel and prints a https://….trycloudflare.com URL.
  3. With TUNNEL_NAME set, it runs that named tunnel from ~/.cloudflared/config.yml (or CLOUDFLARED_CONFIG).
  4. You point OAuth redirect URIs / webhooks / APP_URL at the public HTTPS origin — not http://localhost.

Use cases

Scenario Why a tunnel helps
OAuth callbacks Providers reject http://localhost or require HTTPS
Webhooks External services POST to a reachable URL
Share WIP Teammate opens your branch without VPN
Mobile testing Phone hits the same app over HTTPS
Laravel + Google OAuth Lab Connect flows need a stable-enough HTTPS APP_URL
Non-Laravel (Node, etc.) Same bin; no framework coupling

Laravel notes

This package is not Laravel-specific. When you use it with Laravel:

Avoid composer run dev + Vite HMR behind the tunnel

composer run dev / php artisan dev typically serves the app and Vite on localhost:5173. The tunnel can reach your app origin, but the browser on https://….trycloudflare.com still tries to load assets from http://localhost:5173mixed content / broken CSS & JS.

Recommended flow (OAuth / webhooks)

  1. Build assets: npm run build
  2. Serve the app only: php artisan serve (or Herd’s PHP site)
  3. In another terminal: vendor/bin/cloudflare-tunnel (or composer run tunnel)
  4. Set APP_URL to the printed public HTTPS URL, then php artisan config:clear and restart the app server
  5. Point OAuth redirect URIs at ${APP_URL}/…

Do not fold the tunnel into default composer run dev: quick tunnels mint a new hostname every start (breaking APP_URL / redirect URIs), and public exposure should stay opt-in.

Herd alternative

Herd users may prefer herd share for Vite-aware public sharing instead of (or alongside) this helper.

License

MIT © Solution Forest