ApacheサーバーでのCORS設定がうまく反映されない

### 背景

Webアプリケーションが他のドメインからのリクエストを受け付けるためには、CORS設定が適切に構成されている必要があります。また、セキュリティを強化するために、HTTPからHTTPSへのリダイレクトを設定することが一般的です。今回、Apacheサーバーにおいて、これらが正しく機能していない状況が発生しました。

### 問題の発生

Apacheサーバーには、以下のような設定が施されていました。この設定では、HTTPからHTTPSへのリダイレクトとCORSヘッダーの設定が含まれています。

```apache
<VirtualHost *:80>
    ServerName www.example.com
    DocumentRoot /var/www/html/myapp

    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 www.example.com
    DocumentRoot /var/www/html/myapp

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/www.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/www.example.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/www.example.com/chain.pem

    <Directory /var/www/html/myapp>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    <IfModule mod_headers.c>
        Header set Access-Control-Allow-Origin "https://www.example.com"
        Header set Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE, PATCH"
        Header set Access-Control-Allow-Headers "Authorization, Content-Type"
        Header set Access-Control-Allow-Credentials "true"
    </IfModule>

    ProxyPass /api http://localhost:3000/
    ProxyPassReverse /api http://localhost:3000/

    <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>
```

この設定は、HTTPアクセスをすべてHTTPSにリダイレクトし、HTTPS経由でのリクエストに対してCORSヘッダーを設定するものでした。具体的には、`Header set Access-Control-Allow-Origin "https://www.example.com"` として、`https://www.example.com` からのリクエストを許可するように設定していました。

#### リクエストの送信と出力結果

この設定が正しく機能しているかを確認するために、以下の `curl` コマンドを使用しました。

```bash
curl -X OPTIONS -I https://www.example.com/api/users/create -H "Origin: https://www.example.com/"
```

- `-X OPTIONS`: `OPTIONS` メソッドを使用してサーバーにリクエストを送信します。
- `-I`: ヘッダー情報のみを取得します。
- `-H "Origin: https://www.example.com/"`: リクエストのオリジン（起点）として `https://www.example.com/` を指定します。

このコマンドを実行した結果、以下のヘッダー情報が返されました。

```bash
HTTP/1.1 200 OK
Date: Thu, 29 Aug 2024 06:22:28 GMT
Server: Apache/2.4.59 (Ubuntu)
Access-Control-Allow-Origin: http://www.example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE, PATCH
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Allow-Credentials: true
Content-Length: 583
Content-Type: text/html; charset=iso-8859-1
```

#### 出力の問題点

この出力を見ると、`Access-Control-Allow-Origin` ヘッダーが `http://www.example.com` として返されており、設定ファイルで指定した `https://www.example.com` ではありませんでした。この不一致は、HTTPSでのリクエストが許可されていないことを示しており、ブラウザ側でCORSエラーが発生する可能性があるため、セキュリティ上の問題となり得ます。

#### 原因の特定

この問題の原因を探るため、他のApache設定ファイルとの競合が疑われました。特に、デフォルトの設定ファイルである `000-default.conf` と `default-ssl.conf` を確認し、同様のCORS設定がされていないかを調べました。

#### 競合の発見

以下は、確認した `000-default.conf` と `default-ssl.conf` の内容です。

**000-default.conf:**

```apache
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html/myapp

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    # CORSヘッダーの追加
    Header set Access-Control-Allow-Origin "http://www.example.com"
    Header set Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE, PATCH"
    Header set Access-Control-Allow-Headers "Authorization, Content-Type"
    Header set Access-Control-Allow-Credentials "true"

    # Preflightリクエストを処理
    <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteCond %{REQUEST_METHOD} OPTIONS
        RewriteRule ^(.*)$ $1 [R=200,L]
    </IfModule>

    # HTTPからHTTPSへのリダイレクト
    RewriteEngine On
    RewriteCond %{HTTPS} !=on
    RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R=301,L]
</VirtualHost>
```

**default-ssl.conf:**

```apache
<VirtualHost *:443>
    ServerName www.example.com
    DocumentRoot /var/www/html/myapp

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/www.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/www.example.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/www.example.com/chain.pem

    # CORSヘッダーの追加
    <IfModule mod_headers.c>
        Header always set Access-Control-Allow-Origin "http://www.example.com"
        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 /var/www/html/myapp>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # アプリケーションへのプロキシ設定
    ProxyPass /api http://localhost:3000/
    ProxyPassReverse /api http://localhost:3000/

    # 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` ヘッダーが `http://www.example.com` に設定されていました。これが、競合の原因となり、HTTPSの設定で意図していたCORS設定が正しく反映されなかった理由でした。

### 解決策

競合を解消するために、`default-ssl.conf` ファイルを修正し、`Access-Control-Allow-Origin` ヘッダーを `https://www.example.com` に設定するように変更しました。以下が修正後の設定ファイルの内容です。

#### 修正後の `default-ssl.conf`

```apache
<VirtualHost *

:443>
    ServerName www.example.com
    DocumentRoot /var/www/html/myapp

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/www.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/www.example.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/www.example.com/chain.pem

    # CORSヘッダーの追加
    <IfModule mod_headers.c>
        Header always set Access-Control-Allow-Origin "https://www.example.com"
        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 /var/www/html/myapp>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # アプリケーションへのプロキシ設定
    ProxyPass /api http://localhost:3000/
    ProxyPassReverse /api http://localhost:3000/

    # 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>
```

### 設定の反映と確認

修正後、Apacheサーバーを再起動して設定を反映させました。

```bash
sudo systemctl restart apache2
```

その後、再度 `curl` コマンドを実行して設定が正しく反映されているかを確認しました。出力は以下の通りです。

```bash
HTTP/1.1 200 OK
Date: Thu, 29 Aug 2024 06:30:22 GMT
Server: Apache/2.4.59 (Ubuntu)
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE, PATCH
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Allow-Credentials: true
Content-Length: 583
Content-Type: text/html; charset=iso-8859-1
```

CORS設定が正しく反映され、`https://www.example.com` からのリクエストが許可されるようになりました。

### 結論

今回のケースでは、Apache設定ファイル間の競合により、意図したCORS設定が適用されていないことが問題でした。このような競合は、設定ファイルを詳細に確認し、競合を解消することで解決可能です。もし同様の問題に直面した場合、設定ファイルの内容を慎重に確認し、競合がないかを確認することが重要です。