Biggest Blunder

APIのCORSでハマってました。。。

以下、備忘録。。。

バックエンド

1.本番のフォルダ構成に変更

publicフォルダ以外のフォルダを本番環境のpublic_html(公開)フォルダに置かない。

/laravel-core/ ……public以外のフォルダとファイル
/public_html/laravel-public/ ……public内のファイル

public/index.phpのパスの変更

__DIR__.'/../../laravel-core/storage/framework/maintenance.php'
__DIR__.'/../../laravel-core/vendor/autoload.php'
__DIR__.'/../../laravel-core/bootstrap/app.php'

app/providers/AppServiceProvider.phpのpublicフォルダのパスの変更

    public function register(): void
    {
        $this->app->bind('path.public', function () {
            return base_path('../public_html/laravel-public');
        });
    }

2..envファイルの本番環境の情報に書き換え

バックエンドhttps://domein.com、フロントエンドhttps://sub.domein.comとして。


APP_DEBUG=false
APP_URL=https://domain.com

FRONTEND_URL=https://sub.domein.com
CORS_ALLOWED_ORIGINS=https://sub.domein.com,https://domein.com
SANCTUM_STATEFUL_DOMAINS=sub.domein.com,domein.com
SESSION_DOMAIN=.sub.domein.com

DB_CONNECTION=mariadb
DB_HOST=【DBホスト】
DB_PORT=【ポート】
DB_DATABASE=【DB名】
DB_USERNAME=【ユーザー名】
DB_PASSWORD=【パスワード】

3.サーバーへの個別対応

フリーサーバーでapiフォルダおよびURLが使えないと判明した為、routes/api.phpの内容をrutes/web.phpに移す。

Route::post('/custom-url', [CustomApiController::class, 'store']);

4.本番環境の為のCORSの修正

config/cors.phpを本番環境用に修正。

allowed_origins(CORS_ALLOWED_ORIGINS)にバックエンドのURLを入れているのは念のためです。

return [

    'paths' => ['api/*', 'sanctum/csrf-cookie', 'custom-url'],

    'allowed_methods' => ['*'],

    'allowed_origins' => explode(',', env('CORS_ALLOWED_ORIGINS', 'http://localhost:8000')),

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

];
設定項目詳細
pathsCORSを適用するURIパターン
allowed_methods許可するHTTPメソッド
allowed_originsアクセスを許可するオリジン
※末尾のスラッシュに注意
allowed_origins_patterns正規表現によるオリジン指定
allowed_headers許可するリクエストヘッダー
exposed_headersブラウザ側(JavaScript)で読み取りを許可するレスポンスヘッダー
max_ageプリフライト(OPTIONS)リクエストの結果をキャッシュする秒数
supports_credentialsCookieや認証情報(Authorizationヘッダーなど)の送信を許可するかどうか

bootstrap/app.phpのミドルウェアに記述を追加。

    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->prepend(\Illuminate\Http\Middleware\HandleCors::class);
        $middleware->preventRequestForgery(except: [
            '*/custom-url',
        ]);
    })
メソッド
prepend(\Illuminate\Http\Middleware\HandleCors::class)CORS制御を行うための組み込みミドルウェアをグローバルミドルウェアの最初に実行されるよう追加
preventRequestForgery(except: [...])CSRF 保護(preventRequestForgery)の除外 ※1

※1 以下のURLの場合はCSRF 保護の除外が可能。

  • 外部サービスからのWebhook受信: Stripe、PayPal、LINE、GitHubなど、外部システムからデータを受け取るURL。
  • 外部に公開するAPIエンドポイント: クッキーによるセッション認証を使わず、APIトークン(SanctumやPassportなど)で認証を行うURL。

5.Laravelのデプロイ前のコマンド

ライブラリをインストール

composer install --no-dev --optimize-autoloader
オプション詳細
--optimize-autoloaderSR-4などのオートローダーをクラスマップ方式に変換し、静的なクラスマップを生成。クラスを読み込むたびにファイルシステムを検索する処理を省くため、実行速度が向上。
--no-devrequire-dev に記載されている開発用パッケージ(テストツールやデバッグツールなど)のインストールをスキップし、除外。不要なコードを本番環境に入れないためのセキュリティと軽量化につながる。

Windows11のスマートアプリコントロールが以下のファイルをブロックしてしまい、Warningになってしまいますが、実行は正常に完了するので無視。

PHP\8.5.12-1\nts\x64\ext\php_zip.dll

キャッシュのクリア

そのまま行うとエラーが出てしまうので一旦、.envの以下の値をdatabeseからfileに書き換える

CACHE_DRIVER=file
SESSION_DRIVER=file

以下のコマンドでキャッシュのクリア後、.envを戻しておく。

php artisan optimize:clear

Windows11のスマートアプリコントロールが以下のファイルをブロックしてしまい、Warningになってしまいますが、実行は正常に完了するので無視。

PHP\8.5.12-1\nts\x64\ext\php_zip.dll

キャッシュの作成

php artisan optimize

以下のコマンドが順番に実行され、キャッシュファイルが生成されます。

コマンド詳細
php artisan config:cache設定ファイル(config/*.php)を1つのファイルにまとめ、読み込みを高速化。
php artisan event:cacheイベントとリスナーの対応関係をキャッシュします。
php artisan route:cacheルーティング(routes/*.php)をキャッシュし、大量のルートがある場合の登録処理を高速化。
php artisan view:cacheBladeテンプレートを事前にコンパイルし、リクエスト時のコンパイル処理を省く。

Windows11のスマートアプリコントロールが以下のファイルをブロックしてしまい、Warningになってしまいますが、実行は正常に完了するので無視。

PHP\8.5.12-1\nts\x64\ext\php_zip.dll

6.本番環境にアップロード

FTPでもファイルマネージャでも。フリーサーバーなのでSSHでコマンドを叩けない!

以下のフォルダ(実際にあったもののみ掲載)を削除。

対象(ファイル/フォルダ)理由・注意点
tests/ユニットテスト用のコードです。本番の動作には不要です。
storage/logs/*.logローカルのエラーログ
storage/framework/cache/data/*ローカルのキャッシュ
storage/framework/sessions/*ローカルのセッションデータ
storage/framework/views/*コンパイル済みのBladeキャッシュ
bootstrap/cache/*.phpローカルのパス情報が含まれるため、削除
resources/js/
resources/css/
vite.config.js
package.json
package-lock.json
phpunit.xml
.editorconfig
.gitattributes
.env.example
AGENT.md
CLOUD.md
README.md
開発者向けのドキュメントやツール設定ファイル。

7.ウェブサーバーからの書き込み許可

strageフォルダとbootstrap/chacheフォルダの権限を775(書き込み可能)に変更

8.アクセスの変更

public_html/.htaccessの変更。

# 既存のセキュリティ設定(これだけ残す)
SetEnvIf Request_URI ".*" WpCommentDeny

RewriteEngine On

# 常時SSL化(HTTPS転送)
RewriteCond %{HTTPS} !on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

# Laravel標準のルーティング
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

9.Cron設定

一応したけど、、不要でした。

以下、サーバーのコントロールパネルで毎分に設定。

/usr/bin/php /home/【サーバーID】/【プロジェクトパス】/artisan schedule:run >> /dev/null 2>&1
Cron設定の要不要ケース
必要メールの一斉送信、データベースの定期バックアップ、期限切れデータの自動削除など、routes/console.php に定期実行タスクを定義している場合。
不要通常のWebページ表示やフォーム送信など、ユーザーからのアクセスに応じた処理のみを行う場合。

フロントエンド

1.APIのURLを本番環境のものに変更。

fetchの記述例。バックエンドhttps://domein.com、フロントエンドhttps://sub.domein.comとして。

 
            const baseURL = "https://domein.com";
            fetch(`${baseURL}/laravel-public/custom-url`, {
                method: 'POST',
                mode: "cors",
                cache: "default",
                credentials: 'include',
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json',
                },
                body: JSON.stringify(data)
            })
            .then(response => {
                if (!response.ok) {
                    throw new Error('サーバーエラーが発生しました');
                }
                return response.json();
            })
            .then(data => {
                console.log('成功:', data);
            })
            .catch(error => {
                console.error('エラー:', error);
            });
    });

2.本番用のdistファイルを作成

ビルドして本番用のファイル作成。

npm run build

生成されたdistフォルダの中身をフロントエンドのサーバーへアップロード。

3.再読み込み時に404エラーを出さないようにアクセスを設定

フロントエンドのサーバーのindex.htmlを置く場所に.htaccessファイルを作成。

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-do
RewriteRule ^ index.html [L]

すんごい時間かかった~~漏れがあるかもだけど、、一旦、UP!