6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LaravelブログをCloudflareにデプロイして踏んだ地雷7選

6
Last updated at Posted at 2026-03-28

LaravelブログをCloudflare環境にデプロイしたら踏んだ地雷まとめ💥 — JSON-LDの@がBladeに食われた話ほか

はじめに

バイク中古・新車一括検索プラットフォーム「MotoHub」を個人開発しています。

設計編実装編に続いて、今回はデプロイ編です。ローカルでは完璧に動いていたブログ機能を本番(さくらVPS + Docker + Cloudflare)にデプロイしたら、想定外のエラーが連発しました。

踏んだ地雷を全部書きます。同じ構成の人は参考にしてください。


環境

ローカル:
├── WSL2 (Ubuntu)
├── Docker Desktop
└── Claude Code(CLI)で開発

本番:
├── さくらVPS 4GB
├── Docker (docker-compose)
│   ├── Nginx
│   └── PHP-FPM(Laravel 12)
├── MySQL 8
├── Cloudflare(CDN + HTTPS + WAF + キャッシュ)
└── GitHub(デプロイはgit pull方式)

地雷① JSON-LDの @context がBladeディレクティブとして解釈される

症状: 記事詳細ページで500エラー(ParseError)

syntax error, unexpected end of file, expecting "elseif" or "else" or "endif"
resources/views/blog/show.blade.php:194

原因: JSON-LD構造化データの中に書いた @context@type@id がBladeのディレクティブとして解釈されていました。

<!-- これがBladeディレクティブと衝突する -->
<script type="application/ld+json">
{
    "@context": "https://schema.org",   Bladeが @context を探す
    "@type": "BlogPosting",             Bladeが @type を探す
}
</script>

解決策: @@ でエスケープ。

<script type="application/ld+json">
{
    "@@context": "https://schema.org",
    "@@type": "BlogPosting",
    "@@id": "{{ $post->url }}"
}
</script>

Blade内で @@@ 1文字に変換されるので、出力されるHTMLは正しいJSON-LDになります。

教訓: Bladeテンプレート内でJSON-LDを書く場合、@ で始まるプロパティは全て @@ にエスケープする。これはLaravelの公式ドキュメントにも書いてありますが、JSON-LDを書く時に忘れがちです。


地雷② $this->authorize() が動かない

症状: 記事の編集ページ(/admin/blog/posts/1/edit)で500エラー

Call to undefined method App\Http\Controllers\Admin\BlogPostController::authorize()

原因: Laravel 11以降、Controller 基底クラスに AuthorizesRequests トレイトが自動で含まれなくなりました。

解決策: コントローラに明示的にトレイトを追加。

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class BlogPostController extends Controller
{
    use AuthorizesRequests;  // ← これが必要
    
    public function edit(int $id)
    {
        $post = BlogPost::findOrFail($id);
        $this->authorize('update', $post);  // ← これが動くようになる
    }
}

教訓: Laravel 11+で $this->authorize() を使うなら、AuthorizesRequests トレイトを忘れずに。Laravel 10以前の記事やチュートリアルを参考にしていると、この変更に気づかない。


地雷③ 本番の管理画面で403 Forbidden

症状: ブログの管理画面(/admin/blog/posts)にアクセスすると403。ローカルでは動く。

原因: Gate manage-blog の認可チェックで弾かれていた。本番のユーザーに role='admin' が設定されていなかった。

マイグレーションでは is_admin=true のユーザーを自動的に role='admin' に更新する処理を入れていたが、ユーザーID=1を前提にしていた。本番の管理者ユーザーはID=2だった。

// マイグレーション(ID=1を前提にしていた)
DB::table('users')->where('id', 1)->update(['role' => 'admin']);

// 本番の管理者はID=2 → role が null のまま

解決策: tinkerで手動修正。

docker compose exec app php artisan tinker \
    --execute="App\Models\User::where('id', 2)->update(['role' => 'admin']);"

教訓: マイグレーションでユーザーデータを更新する場合、IDをハードコードしない。is_admin=true のような条件で更新するべき。

// こう書くべきだった
DB::table('users')->where('is_admin', true)->update(['role' => 'admin']);

地雷④ 画像アップロードでPermission denied

症状: 記事を画像付きで保存すると500エラー。

Unable to write to directory: /var/www/storage/app/public/blog/images/2026/03

原因: Docker内のPHP-FPMプロセス(www-data)が storage/app/public/blog/images/ ディレクトリに書き込めない。

解決策: ディレクトリ作成+権限設定。

docker compose exec app mkdir -p storage/app/public/blog/images
docker compose exec app chmod -R 775 storage/app/public/blog/images

教訓: Docker環境ではホスト側とコンテナ側でUID/GIDが一致しないことがある。特に storage/ 配下に新しいサブディレクトリを作る場合は権限を明示的に設定する。


地雷⑤ composer dump-autoload を忘れて「blog namespace not found」

症状: サーバーで php artisan blog:generate-sitemap を実行すると:

There are no commands defined in the "blog" namespace.

ファイルは存在するのにArtisanコマンドが認識されない。

原因: composer dump-autoload を実行していなかった。新しいPHPクラスを追加した後、Composerのオートローダーを更新しないとクラスが見つからない。

# これだけで解決
docker compose exec app composer dump-autoload

教訓: git pull で新しいPHPファイルを追加したら、composer dump-autoload を忘れない。特にArtisanコマンド・ServiceProvider・Observerなどの「自動登録される系」のクラスで発生しやすい。


地雷⑥ git stashの地獄

これが一番やばかった。

症状: 本番でナビゲーションバーにgitのコンフリクトマーカーが表示される。

<<<<<<< Updated upstream ======= ブログ >>>>>>> Stashed changes

image.png

原因の経緯:

  1. サーバーで composer require league/commonmark を実行 → composer.jsoncomposer.lock が変更される
  2. その後 git pull しようとすると「ローカルの変更が上書きされる」エラー
  3. git stash して git pullgit stash pop でstash復元
  4. stash復元時にナビゲーションのBladeファイルでコンフリクト発生
  5. コンフリクトマーカーがそのままコミット・デプロイされてしまった

解決策:

# ローカルでコンフリクトマーカーを検索
grep -rn "<<<<<<" backend/resources/views/components/

# 該当ファイルを修正してcommit & push

教訓:

  • 本番サーバーで composer require しない。ローカルで実行して composer.jsoncomposer.lock をcommitしてからデプロイする
  • git stash pop した後は必ず grep -rn "<<<<<<" . でコンフリクトが残っていないか確認する
  • 本番サーバーでのgit操作は git pull のみ。stash / merge / checkout は極力避ける

地雷⑦ ローカルでpush忘れに気づかない

症状: サーバーで git pull しても何も来ない。php artisan blog:generate-sitemap も動かない。

原因: ローカルで一度もpushしていなかった。Claude Code(CLI)はファイルの作成・編集はしてくれるが、git pushは自動ではやらない

さらに悪いことに、Claude Codeが feature/parts-price-compare ブランチで作業していたため、main ブランチには何もない状態だった。

解決策:

# ブランチの確認
git branch  # ← feature/parts-price-compare にいた

# mainに切り替えてマージ
git checkout main
git merge feature/parts-price-compare
git push

教訓: Claude Codeを使う場合、作業後に以下を確認する習慣をつける:

  1. git status — 未コミットのファイルがないか
  2. git branch — どのブランチにいるか
  3. git log --oneline -3 — コミットされているか
  4. git push — リモートに反映されているか

デプロイチェックリスト(最終版)

上記の地雷を踏まえて作った、MotoHubブログのデプロイチェックリストです。

# === ローカル ===
# 1. ブランチ確認
git branch  # mainにいること

# 2. コンフリクトマーカー確認
grep -rn "<<<<<<" backend/

# 3. commit & push
git add -A
git status  # 差分確認
git commit -m "your message"
git push

# === サーバー ===
# 4. pull
cd /var/www/motohub
git pull

# 5. Composerオートローダー更新(新PHPファイルがある場合)
docker compose exec app composer dump-autoload

# 6. マイグレーション(DBスキーマ変更がある場合)
docker compose exec app php artisan migrate --force

# 7. キャッシュクリア
docker compose exec app php artisan config:cache
docker compose exec app php artisan view:clear
docker compose exec app php artisan route:clear

# 8. サイトマップ再生成
docker compose exec app php artisan blog:generate-sitemap

# 9. Cloudflareキャッシュパージ
# Cloudflareダッシュボードから Purge Everything
# または: docker compose exec app php artisan blog:purge-cache --all

まとめ

地雷 原因 教訓
JSON-LDの@がBladeに食われる Bladeが @context をディレクティブと認識 @@ でエスケープ
authorize()が動かない Laravel 11+でトレイトが自動追加されなくなった AuthorizesRequestsを明示追加
本番で403 Forbidden ユーザーのroleが未設定 IDハードコードしない
画像Permission denied Docker内の権限問題 mkdir + chmod 775
コマンドが見つからない composer dump-autoload忘れ 新PHPファイル追加後は必須
コンフリクトマーカー混入 git stash pop後の確認漏れ grep "<<<<<<" で必ず確認
push忘れ Claude Codeはpushしない 作業後に必ずpush確認

個人開発はCI/CDパイプラインを組むほどでもないけど、手動デプロイだからこそチェックリストが命です。


前回の記事:管理画面はmarked.js、公開画面はleague/commonmark — MarkdownのハイブリッドレンダリングをLaravelで実装した話

MotoHub: https://motohub.jp
MotoHubブログ: https://motohub.jp/blog

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?