永遠にlocalhostがCORSから弾かれた話
ローカルで動作するフロントエンドアプリケーション(http://localhost:3000
)から、別のドメインにあるAPIサーバーにリクエストを送ろうとしていました。しかし、ブラウザのコンソールには以下のようなエラーメッセージが表示され、リクエストが拒否されました。
Access to fetch at 'https://example.com/api/users/create' from origin 'http://localhost:3000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:3000, *', but only one is allowed.
一見すると、Access-Control-Allow-Origin
ヘッダーにhttp://localhost:3000
と*
が同時に設定されていることが問題のように見えます。しかし、なぜこのような状況が発生しているのかを理解するために、原因を探ることにしました。
原因を探る:CORS設定の競合
まず、サーバー側の設定を確認しました。以下が実際に使用していたApacheの設定例です。
<VirtualHost *:80>
ServerName example.com
DocumentRoot /path/to/your/app
# HTTPをHTTPSにリダイレクト
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
DocumentRoot /path/to/your/app
# SSL設定
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
# CORSヘッダーの追加
<IfModule mod_headers.c>
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE, PATCH"
Header always set Access-Control-Allow-Headers "Authorization, Content-Type"
Header always set Access-Control-Allow-Credentials "true"
</IfModule>
<Directory /path/to/your/app>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# アプリケーションへのプロキシ設定
ProxyPass /api http://localhost:3001/
ProxyPassReverse /api http://localhost:3001/
# Preflightリクエストを処理
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
</IfModule>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
この設定ファイルでは、Access-Control-Allow-Origin
ヘッダーを設定するために、Apacheのmod_headers
モジュールを使用していました。この設定により、*
として全てのオリジンからのリクエストを許可するようにしていたのですが、エラーメッセージによると、http://localhost:3000
もヘッダーに含まれていることが問題でした。
この時点で、アプリケーション側でもCORSの設定が行われている可能性が頭をよぎりました。もしアプリケーション側でCORSの設定がされていると、Apacheの設定と競合し、結果としてヘッダーが重複して設定される可能性があります。この仮説をもとに、アプリケーションのCORS設定を再確認することにしました。
問題解決:アプリケーションのCORS設定を無効化する
アプリケーションの設定ファイルを調査したところ、CORSに関連する設定がいくつか見つかりました。具体的には、rack-cors
や類似のミドルウェアが使用されており、これが原因でApacheの設定と競合していたようです。
次に行ったのは、これらの設定の削除です。設定ファイルからCORSに関連する設定をすべて削除し、その後、アプリケーションサーバーを再起動しました。
# サーバー再起動
rails s
サーバー再起動後、再度リクエストを試みると、今度はエラーが発生せず、期待通りにリクエストが成功しました。ApacheのCORS設定が正しく反映され、アプリケーション側の設定との競合が解消されたことを確認しました。
学び:CORS設定の複数レイヤーに注意
今回の経験を通じて学んだのは、CORS設定が複数のレイヤーで競合すると、予期しないエラーが発生する可能性があるということです。特に、フロントエンドとバックエンドが異なるドメインで動作している場合、CORS設定がどのように影響するかを慎重に管理する必要があります。
同じようなCORSエラーに遭遇した場合、まずはCORS設定が複数の場所で重複していないかを確認することが重要です。不要な設定は取り除き、シンプルな構成にすることで、こうした問題を未然に防ぐことができます。