CORSの設定をいじりまくってもエラーが出るあなたへ

## 目次
1. [結論](#結論)
2. [前提](#前提)
3. [事故現場](#事故現場)
4. [CORS設定ファイルの種類](#CORS設定ファイルの種類)
5. [(知識)複数のCORS設定ファイルを作った場合の処理の優先順位](#知識複数のCORS設定ファイルを作った場合の処理の優先順位)
6. [おまけ](#おまけ)


## 結論
CORSの設定ファイルが複数ある場合は設定ファイルを一元管理しましょう。
バックエンド/config/initializers/cors.rb
に記述すればOKです！
他のファイルに記述している場合はコメントアウトしてください。
僕が詰まった原因はこれでした。
以下で実際に起こったエラーや試したこと、調べる中で得た知識を共有しようと思います。

## 前提
開発環境や使っている言語は以下の通りです。
デプロイした本番環境はフロントエンドがVercel、バックエンドがHerokuです。
| 言語・フレームワーク        | バージョン    |
| ----------------------| ---------- |
| React                 | 18.2.0     |
| Next.js               | 14.0.4     |
| Ruby                  | 3.1.4      |
| Rails                 | 7.0.8      |
| Docker                | 24.0.7     |

## 事故現場
事件はアカウントの新規登録をしようと、フロントエンドからバックエンドにaxios.postする際に起こりました。
フロントエンドの記述がこちら
```typescript:auth.tsx
  try {
    const response = await axios.post(`${process.env.NEXT_PUBLIC_API_BASE_URL}/signup`, {
      user: {
        email: args.email,
        password: args.password,
        password_confirmation: args.passwordConfirmation,
        name: args.name,
      },
    },{
      withCredentials: true
    });
```
この画面で「新規登録」ボタンを押していざアカウント登録をしようと思った時にエラーが出ました。
![スクリーンショット 2024-06-11 13.47.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812420/edc3a9d6-a469-f6e0-2d84-82b2e4d0c0ba.png)

実際に出力されたエラーがこちら
```:error
Access to XMLHttpRequest 
at 'https://one-drink-2024-e34bfb30684c.herokuapp.com/signup' 
from origin 'https://one-drink.vercel.app' 
has been blocked by CORS policy: 
The value of the 'Access-Control-Allow-Origin' header 
in the response must not be the wildcard '*' 
when the request's credentials mode is 'include'. 
The credentials mode of requests initiated by the XMLHttpRequest 
is controlled by the withCredentials attribute.
```

エラーの内容は、バックエンドとの通信を行う際に
```typescript
withCredentials: true
```
を用いる場合は、「Access-Control-Allow-Origin」ヘッダに「\*(ワイルドカード)」を指定できないというものです。
この場合、「\*」ではなくフロントエンドの具体的なURLをCORSファイルに設定すれば解決するはずでした。
しかし、このエラーが出るに至るまでにも別のCORSエラーが発生しており、様々な設定ファイルを作って記述しまくっていたのが今回ハマった原因です。
もう一度書きますが、CORSの設定ファイルを一元管理すれば解決できました。

## CORS設定ファイルの種類
フロントエンドとバックエンドでCORSの設定ができる場所がいくつかあります。

**フロントエンド**
- vercel.json
- API Routes(pages/apis/xxx-api.ts)
- next.config.js
- server.js

**バックエンド**
- /config/initializers/cors.rb
- /config/application.rb
- /config/xxx(開発環境、本番環境ごとに設定を分けたい場合)
    - xxx → development.rb 開発環境
    - xxx → production.rb 本番環境
- 各コントローラ(もしくは application_controller.rb)

僕が各設定ファイルに記述していた内容は以下の通りです。
```ruby:CORS設定
    config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins 'https://one-drink.vercel.app'
        resource '*',
          headers: :any,
          methods: [:get, :post, :put, :patch, :delete, :options, :head],
          expose: ['Access-Control-Allow-Origin']
          max_age: 600,
          credentials: true
      end
    end
```
「origins」に具体的なURLを設定すれば解決できるということで設定をいじっていましたが「\*になってるよー」というエラーがずっと出続けており、その度に設定ファイルを増やしていくという悪循環に陥っていました。
実際にはフロントにvercel.jsonを作ったのを忘れており、そこの「origins」が「\*」になっていたのがクリティカルな原因でした...:sweat_smile:

## (知識)複数のCORS設定ファイルを作った場合の処理の優先順位
CORSの設定ファイルを複数作ってしまった場合の処理の優先順位があるらしいので、書いておきます。

**フロントエンド**
- Vercelにデプロイする場合はvercel.jsonが最優先になる
- API Routes内でCORSのヘッダーを設定する場合は、グローバルな設定よりもAPI Routes内の設定が優先される
- カスタムサーバを使用する場合はExpressなどのミドルウェアがNext.jsの設定よりも前に読み込まれる

**バックエンド**
読み込まれる順番
config/application.rb
↓
config/initializers/cors.rb
↓
config/environments/xxx.rb

(コントローラでの設定は個別に設定したい場合に有効。)
基本的に最後に読み込まれたファイルの設定になるので、異なる設定を行っていた場合はエラーが出ずに優先順位の高い設定に上書きされるらしいです。

## おまけ
実際に開発していたファイルとアプリを公開します。
このアプリのアカウント作成画面で新規作成のボタンを押したときにエラーが発生していました。
具体的なソースコードの参考になれば幸いです。
- [GitHub](https://github.com/kazuma1227rinrin/One-Drink)
- [アプリ(One Drink)](https://one-drink.vercel.app)