質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.82%

特定のルートのみReact(JavaScript)からLumen(PHP)へAjaxをした際にCORSで弾かれる。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 577

khkhkh

score 20

概要

現在下記の様な構成のアプリケーションを開発しています。
構成図

フロントエンドにReact.js、バックエンドにAPIサーバーのLumen、その間にリバースプロキシとしてNginxを置いています。
そこで、ReactからPromiseベースのAjaxライブラリであるaxiosを使ったところCORSで弾かれました。
Lumen側でCORSの設定をし、一応は回避することはできたのですが、特定のページにいくとまたCORSで弾かれます。
画面の遷移にはreact-routerを使用しています。個人的にはwebpack、もしくはnginxの設定が間違っていると考えています。
自分なりに色々調べてみましたが、解決せず…。
どなたか解決方法のご教授お願いいたします。

エラーの内容

Access to XMLHttpRequest at 'http://localhost:8000/api/sticky' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

該当のコード

// nginx

server {
    listen       0.0.0.0:80;
    server_name  localhost;
    charset      utf-8;

    location / {
        root    /var/www/client/dist;
        index    index.html index.htm;
        try_files $uri $uri/ /index.html;

        if ($request_method = 'OPTIONS') {
          add_header 'Access-Control-Allow-Origin: $http_origin');
          add_header 'Access-Control-Allow-Origin: GET, POST, DELETE, PUT, PATCH, OPTIONS');
          add_header 'Access-Control-Allow-Credentials: true');
          add_header 'Vary: Origin');
        }

        add_header 'Access-Control-Allow-Origin' "$http_origin" always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
    }

    # Media: images, icons, video, audio, HTC
    location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
        expires 1M;
        access_log off;
        add_header Cache-Control "public";
    }

    # ReverseProxy: Lumen API server
    location ^~ /api {
        proxy_pass http://127.0.0.1:8000;

        location ~ \.php$ {
            fastcgi_pass  server:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include       fastcgi_params;
        }

        if ($request_method = 'OPTIONS') {
          add_header 'Access-Control-Allow-Origin: $http_origin');
          add_header 'Access-Control-Allow-Origin: GET, POST, DELETE, PUT, PATCH, OPTIONS');
          add_header 'Access-Control-Allow-Credentials: true');
          add_header 'Vary: Origin');
        }

        add_header 'Access-Control-Allow-Origin' "$http_origin" always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
    }
}
// Lumen

<?php

namespace App\Http\Middleware;

use Closure;

class CorsMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // TODO:Access-Control-Allow-Originを適切に指定する必要がある
        $headers = [
            'Access-Control-Allow-Origin'      => '*',
            'Access-Control-Allow-Methods'     => 'POST, GET, PUT, DELETE, OPTIONS',
            'Access-Control-Allow-Credentials' => 'true',
            'Access-Control-Max-Age'           => '86400',
            'Access-Control-Allow-Headers'     => 'Content-Type, Authorization, X-Requested-With'
        ];

        // preflightリクエスト用
        if ($request->isMethod('OPTIONS')) {
            return response()->json('{"method":"OPTIONS"}', 200, $headers);
        }

        $response = $next($request);
        foreach ($headers as $key => $value) {
            $response->header($key, $value);
        }

        return $response;
    }
}
// webpack.config.js

import path from 'path'

const src = path.resolve(__dirname, 'src')
const dist = path.resolve(__dirname, 'dist')

module.exports = {
    mode: "development",
    entry: ['@babel/polyfill', src + '/index.js'],
    output: {
        path: dist,
        filename: "main.js"
    },
    module: {
        rules: [
            {
                // css
                test: /\.css$/,
                exclude: {
                    include: /node_modules/,
                    // quill.js
                    exclude: /node_modules\/react-quill\//
                },
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            url: false,
                        },
                    },
                ],
            },
            {
                // js
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: [['@babel/preset-env', { modules: false }]]
                        }
                    }
                ]
            },
            {
                // tsx
                test: /\.tsx?$/,
                use: "ts-loader",
            },
            {
                // image
                test: /\.(jpg|JPG|jpeg|png|PING|gif|mp3|svg|ttf|woff2|woff|eot)$/,
                use: {
                    loader: "file-loader",
                    options: {
                        name: "[name].[ext]",
                        outputPath: "assets/img",
                        publicPath: path => "/assets/img/" + path
                    }
                }
            }
        ]
    },
    resolve: {
        extensions: [".ts", ".tsx", ".js", ".json"]
    },
    devServer: {
        contentBase: './dist',
        host: '0.0.0.0',
        port: 3000,
        inline: true,
        historyApiFallback: true
    }
};

バージョン

Lumen 6.3.3

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • khkhkh

    2020/03/13 05:20

    postmanで叩いてみましたが、正常にレスポンスが帰ってきました…。この現象は一体なんなんでしょうか…?

    キャンセル

  • khkhkh

    2020/03/13 05:26

    正常に動作いたしました!!!
    php側でエラーが発生していました…。本当に申し訳御座いません…。
    ただ、エラーとCORSにどういった関係があるのかが、よくわかりません…。
    とりあえず、最後までご丁寧なご対応ありがとうございました!

    キャンセル

  • hoshi-takanori

    2020/03/13 05:29

    PHP 側でエラーが発生すると、CORS のヘッダーを設定する前にエラーメッセージを出力しちゃうので、ヘッダーが正常に出力されず、CORS エラーってことになるのでしょうね。CORS エラーな時点で中身(エラーメッセージも)は無視されるでしょうし。

    キャンセル

回答 1

check解決した方法

+1

PHPのエラーを修正したところ正常に動作いたしました!

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.82%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る