Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPer、Cloudflare に引っ越す

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

PHPer、Cloudflare に引っ越す

Avatar for Suguru Ohki

Suguru Ohki

April 24, 2026

More Decks by Suguru Ohki

Other Decks in Programming

Transcript

  1. 自己紹介 スー(@SuguruOoki) MOSH 株式会社 Laravel Live Japan Core Staff GoToMarket

    / HROps / SalesOps / 技術 広報 https://blog.sue-san.dev よろしくお願いします Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 2
  2. 半年前の私 「Cloudflare? それは JavaScript の世界。 PHPer が行く場所じゃない」 — そう思ってました。 でも、2026

    年のいま。 私の Laravel は 世界330都市で動いて います。 Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 3
  3. 今日壊す 3つの先入観 × 3つのアプローチ 先入観 解決アプローチ 本日の扱い PHPランタイム無いでしょ? A. php-wasm

    軽く触れる Laravel動かないでしょ? B. Containers + FrankenPHP 本命・デモあり 長時間処理できないでしょ? + Cloudflare Queues Real World で → 迷ったら B。今日の 20 分で、全部壊します Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 4
  4. A. php-wasm — PHP を Wasm 化して Worker で動かす Laravel

    Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 5
  5. A. アーキテクチャ HTTP Browser Cloudflare Worker V8 isolate + php-wasm

    D1 DB代替 R2 ファイル KV セッション PHP インタプリタを Wasm にコンパイル Workers の V8 isolate 上で実行 採用実績: WordPress.org 公式 Playground Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 6
  6. A. 書くのは PHP だけ・得手不得手も正直に <?php // public/index.php ← これ1ファイル header('Content-Type:

    application/json'); echo json_encode(['msg' => 'Hello from PHP', 'v' => PHP_VERSION]); wrangler deploy # → 世界330都市に即配布 得意: コールドスタートゼロ / 無料枠10万req/日 / WordPress 苦手: pdo_mysql 等の拡張 / FS揮発 / Laravel フルスタック → だから本命は B Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 7
  7. B. Containers — FrankenPHP で Laravel がそのまま動く Laravel Live Japan

    2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 8
  8. B. 最小 Dockerfile(これだけで Laravel が動く) FROM dunglas/frankenphp:php8.4-alpine RUN install-php-extensions \

    pdo_mysql pdo_sqlite redis intl bcmath pcntl WORKDIR /app COPY . . RUN composer install --no-dev --optimize-autoloader # Worker モード = Laravel Octane 相当 ENV FRANKENPHP_CONFIG="worker ./public/index.php" PHPer が既に書ける水準 = 学習コストほぼゼロ Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 9
  9. B. Laravel の .env 書き換えポイント 項目 変更内容 DB_CONNECTION mysql →

    sqlite (D1 プロキシ経由) FILESYSTEM_DISK local → s3 (R2 互換) SESSION_DRIVER file → database / redis (KV) CACHE_STORE file → database / redis (KV) QUEUE_CONNECTION database → cloudflare コード変更は .env だけで済むケースが多い Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 10
  10. Live Demo: 本当に Octane が動く # そのままコピペで叩けます URL=https://cloudflare-laravel-octane-demo.suguru-ohki.workers.dev/api/hello for i

    in {1..5}; do curl -s $URL | jq -c '{request_count_this_worker, worker_pid, laravel_version}' done {"request_count_this_worker":2,"worker_pid":524,"laravel_version":"13.6.0"} {"request_count_this_worker":3,"worker_pid":524,"laravel_version":"13.6.0"} {"request_count_this_worker":4,"worker_pid":524,"laravel_version":"13.6.0"} {"request_count_this_worker":5,"worker_pid":524,"laravel_version":"13.6.0"} {"request_count_this_worker":6,"worker_pid":524,"laravel_version":"13.6.0"} pid=524 不変 × カウンタが増加 = Octane ワーカーが常駐している証拠 Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 11
  11. B. ベンチ & コスト(月10万PV想定) レイテンシ EC2 t3.small (ap-northeast-1): TTFB 45ms

    CF Containers + FrankenPHP: TTFB 18ms 月額 項目 EC2構成 CF構成 コンピュート $15 $3 DB $12 (RDS) $2 (D1) 転送 $9 (S3 egress) $0 (R2) 合計 $36 $5 → 約 1/7 のコスト で動く Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 12
  12. C. Worker を "賢い CDN" として前段に Worker(エッジ): 認証 / キャッシュ

    / A/B / ジオルーティング 既存 Laravel(オリジン): 1 行も変えない // routes/api.php (Laravel 側) Route::middleware('auth:sanctum')->group(function () { Route::post('/orders', fn (Request $r) => Order::create([ 'user_id' => $r->user()->id, 'items' => $r->json('items'), ])); }); Sanctum / Passport がそのまま動く = 認証資産を捨てなくていい Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 14
  13. Yes No Yes No START Laravel 等 フルスタック? B: Containers

    + FrankenPHP 既存オリジン あり? C: ハイブリッド A: php-wasm A / B / C ここまでのまとめ 要件 A B C WordPress / 軽量API ◎ ◎ ◦ Laravel + Octane △ ◎ 実証済 ◎ 既存本番の高速化 ✗ △ ◎ コールドスタート ◎ ◦ ◎ 迷ったら B。新規小規模なら A、既存活 かすなら C Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 15
  14. Live Demo 2: 認証ありAPI + Cache-Control URL=https://cloudflare-laravel-octane-demo.suguru-ohki.workers.dev # 1. 認証なし

    → 401 curl -s -H "Accept: application/json" $URL/api/me # → {"message":"Unauthenticated."} # 2. ログイン → Sanctum トークン取得 TOKEN=$(curl -s $URL/api/login \ -H "Content-Type: application/json" -H "Accept: application/json" \ -d '{"email":"[email protected]","password":"demo-password"}' | jq -r .token) # 3. 認証あり → 200 + ユーザー情報 curl -s $URL/api/me -H "Authorization: Bearer $TOKEN" | jq # 4. レスポンスヘッダ → Laravel 側の防御が効いている証拠 curl -sI $URL/api/me -H "Authorization: Bearer $TOKEN" | grep -i cache-control # → Cache-Control: no-store, private Sanctum がそのまま動く × Cache-Control で CDN キャッシュ禁止 → でも もし これが無かったら? Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 16
  15. キャッシュが認証データを漏らす事故 絶対避けたい事故 Worker が GET /api/me を URL だけでキャッシュ User

    A の応答が User B に返る → 個人情報漏洩 / 認証バイパス 安全な3パターン 対象 戦略 公開 GET そのままキャッシュ OK 認証 GET Authorization / セッション あり → bypass 認証 GET を効かせたい稀ケース cache key に ユーザーID 混ぜる Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 18
  16. Laravel 側: Cache-Control で明示 return response()->json($data) ->header('Cache-Control', 'private, no-store'); 効き目

    private : CDN / プロキシのキャッシュ 禁止 no-store : あらゆるキャッシュ 禁止 Cloudflare は RFC 9111 準拠 なので、Laravel 側だけで全レイヤに効く 基本は これだけ で OK Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 19
  17. Email: Cloudflare Email Service (2025/10 Beta) // 既存の Laravel Mail

    はそのまま Mail::to($user->email)->send(new WelcomeMail($user)); 初回セットアップ(1回だけ) composer require symfony/http-client # Worker へ POST するクライアント カスタム Transport を app/Mail/Transports/ に配置(Appendix B0) .env で MAIL_MAILER=cloudflare に切り替え → 以降は既存 Mail クラスがそのまま Cloudflare 経由に Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 21
  18. Queue: dispatch() がそのまま流れる // .env: QUEUE_CONNECTION=cloudflare use App\Jobs\GenerateMonthlyReport; GenerateMonthlyReport::dispatch($userId) ->onQueue('php-heavy-jobs');

    実行枠 CPU 5分 / wall 15分 — HTTPの30秒制限を最大30倍に拡張 失敗時は 自動リトライ + DLQ Consumer の実装は Appendix A1-A2 参照 Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 22
  19. ハマりポイント TOP3(実体験・抜粋) 1. session_start() のファイルセッション → KV / DB へ

    2. storage/logs 揮発 → stdout + Logpush 3. pdo_mysql 前提 → D1 HTTP API / TiDB Serverless 初日の詰まりを先回りで消せる(残り2件は Appendix A7) Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 23
  20. 冒頭の3つの先入観、全部壊しました 先入観 解 PHP ランタイム無い php-wasm で動く Laravel 動かない Containers

    + FrankenPHP で実証済 長時間処理できない Queues で CPU 5分 / wall 15分 そして 半年後の私の Laravel は、世界330都市で動いている Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 24
  21. 今日持ち帰ってほしい3つ 1. PHP は Cloudflare で 動く 2. Laravel なら

    Containers + FrankenPHP が本命 3. 「無いパーツ」は Laravel 流儀のまま差し替えるだけ Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 25
  22. Laravel Live Japan のご紹介 日本唯一の Laravel 専門カンファレンス 2026年 5月26日(火)〜 27日(水)

    開催 Taylor Otwell も来日実績、海外コミッター登壇常連 Octane / Livewire / Inertia / Reverb の深掘り Day 関西 PHPer にこそ来てほしい3つの理由 1. 関西からの登壇枠が毎年複数 — 東京一極集中じゃない 2. 前夜祭で Laravel 本家コアコミッターから日本語で一次情報 3. Core Staff 募集中(関西リモート大歓迎 ) laravellive.jp / @LaravelLiveJP 関西 PHP 勉強会の延長線に、もう一段深い Laravel コミュニティがあります Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 26
  23. 今夜の 5 分で、あなたの Laravel が330都市に # 1. wrangler をインストール(初めての方) npm

    install -g wrangler wrangler login # 2. サンプルを clone してデプロイ git clone https://github.com/SuguruOoki/cloudflare-php-demo cd cloudflare-php-demo/apps/frankenphp-container wrangler deploy リソース GitHub: github.com/SuguruOoki/cloudflare-php-demo Live: cloudflare-laravel-octane-demo.suguru-ohki.workers.dev Docs: developers.cloudflare.com/containers Thank you! — ご質問をどうぞ Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 27
  24. A1: Queue 詳細 — batch_size / batch_timeout 配送の 2 つのダイヤル

    設定 意味 既定 / 最大 max_batch_size 1 回で受け取る最大件数 10 / 100 max_batch_timeout 満杯未満でも配送される秒数 5 / 60 配送ルール size に達する OR timeout 経過 のどちらか 先に来た方 でバッチ確定 Consumer 実行時間枠 CPU: 既定 30秒 → 最大 5分 Wall clock: 最大 15分 Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 29
  25. A2: Queue Consumer 実装(Laravel 側) // routes/web.php などに Worker からの

    POST を受ける口 Route::post('/process', function (Request $r) { $kind = $r->json('kind'); if ($kind === 'monthly-report') { GenerateMonthlyReport::dispatchSync($r->json('args.user_id')); } return response()->json(['status' => 'processed']); }); DLQ(Dead Letter Queue) Laravel failed_jobs テーブルと同じ役割。違いは DLQ 自体もキュー なので「失敗 ジョブ再実行」を Consumer 追加だけで実装できる。 Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 30
  26. A3: キャッシュ Cache-Control 仕様(RFC 9111) ディレクティブ 意味 誰が禁止される? private single-user

    向け CDN / プロキシのみ 禁止 no-store あらゆるキャッシュ禁止 全員(ブラウザ含む) public 共有キャッシュ OK — no-cache 保存OKだが毎回再検証 — Cloudflare は仕様に従うので、Laravel で private, no-store を書く だけで全レイヤに効く Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 31
  27. A4: Worker 側の二重防御(belt and suspenders) 契約が破られる稀なケース 1. Cache Rules で

    "Cache Everything" 強制(Enterprise設定ミス) 2. Worker コードで Cache-Control を剥がしてから caches.default.put() 3. 壊れた中間プロキシ Worker 側: 認証ヘッダを見たら即 bypass if (req.headers.has('Authorization')) return bypass(); if (/laravel_session|auth/i.test(cookie)) return bypass(); Laravel 側の private, no-store と Worker 側の shouldBypassCache() の両方揃 って初めて全レイヤで漏れない。 Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 32
  28. B0-①: Cloudflare Mail Transport — 準備と登録 1. 必要ライブラリ composer require

    symfony/http-client ※ Laravel 標準の Http:: ファサードが内部で利用 2. Transport 登録 // AppServiceProvider::boot Mail::extend('cloudflare', fn () => new CloudflareMailTransport( workerUrl: config('services.cloudflare.worker_url'), )); → 実装クラスは次ページ(B0-②) Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 33
  29. B0-②: Cloudflare Mail Transport — 実装クラス // app/Mail/Transports/CloudflareMailTransport.php namespace App\Mail\Transports;

    use Symfony\Component\Mailer\Transport\AbstractTransport; use Symfony\Component\Mailer\SentMessage; use Illuminate\Support\Facades\Http; class CloudflareMailTransport extends AbstractTransport { public function __construct(private string $workerUrl) {} protected function doSend(SentMessage $message): void { Http::post($this->workerUrl, [ 'raw' => $message->toString(), ])->throw(); } public function __toString(): string { return 'cloudflare'; } } Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 34
  30. A5: デバッグ・モニタリング ローカル開発 wrangler dev → Container も起動可(php artisan serve

    感覚) 本番 wrangler tail → リアルタイムログ(tail -f 感覚) Logpush → S3 / R2 / BigQuery へ構造化ログ排出 Analytics Engine → カスタムメトリクス エラートラッキング Sentry PHP SDK がそのまま動く Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 35
  31. A7: ハマりポイント(本編 TOP3 の残り) 4. ext-gd / ext-imagick → Dockerfile

    で install-php-extensions に追加 5. タイムゾーン → config/app.php timezone => 'Asia/Tokyo' or date_default_timezone_set() 本編 TOP3(再掲) 1. session_start() → KV / DB へ 2. storage/logs 揮発 → stdout + Logpush 3. pdo_mysql 前提 → D1 HTTP API / TiDB Serverless Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 36
  32. A6: 参考リンク 分類 リンク Cloudflare Docs developers.cloudflare.com/{containers, email-service, queues} Cache

    Control developers.cloudflare.com/cache/concepts/cache-control HTTP 標準 RFC 9111 HTTP Caching 関連 OSS frankenphp.dev / laravel.com/docs/octane 本日のサンプル github.com/SuguruOoki/cloudflare-php-demo Laravel Live Japan 2026 / PHPer、Cloudflare に引っ越す 2026 / @SuguruOoki 37