2026年8月5日

2026年8月5日

Apacheでクロスオリジン(CORS)を設定する方法|許可オリジンの限定とWordPress REST APIの注意点

Apache で CORS を許可するには、mod_headers の Header 命令で Access-Control-Allow-Origin などのレスポンスヘッダーを付けます。ただし、何でも * で許可すると不要な公開につながり、WordPress の REST API では WordPress 自身が CORS ヘッダーを出すため、Apache 側で重ねると逆にエラーになることがあります。この記事では、CORS の仕組みを押さえたうえで、静的ファイル向けの基本設定、複数のオリジンを限定して許可する書き方、プリフライトへの対応、REST API での注意点と確認方法を説明します。

CORS が必要になる場面

ブラウザには、あるサイト(オリジン)から読み込んだ JavaScript が、別のオリジンのデータを勝手に読み取れないようにする同一オリジンポリシーがあります。オリジンは「スキーム+ホスト名+ポート」の組み合わせで、https://example.com と https://www.example.com、http://example.com はそれぞれ別のオリジンです。

CORS は、データを持つ側のサーバーが「このオリジンからの読み取りは許可する」とレスポンスヘッダーで宣言する仕組みです。WordPress の運用では、次のような場面で必要になります。

  • Web フォントを別ドメイン(画像用のサブドメインや CDN)から配信している。フォントの読み込みには CORS の許可が必要です
  • 別ドメインのフロントエンド(ヘッドレス構成のアプリなど)から、fetch で WordPress の REST API を呼ぶ
  • 別サイトの JavaScript から JSON ファイルや画像を canvas で加工する

ブラウザの開発者ツールのコンソールに「has been blocked by CORS policy」のようなメッセージが出ていれば、CORS の設定が足りていません。メッセージの後半に、足りないヘッダーの名前が書かれていることが多いので、まずはそこを読みます。

CORS はブラウザが守るルールであり、サーバーを守るアクセス制限ではない点も押さえておきます。curl やサーバー間の通信は CORS を無視するため、公開したくないデータは認証や権限チェックで守る必要があります。

作業前の準備

Header 命令を使うには mod_headers が必要です。Debian/Ubuntu 系では有効になっていないことがあります。

sudo apache2ctl -M | grep headers
# 表示されなければ有効にする(Debian/Ubuntu系)
sudo a2enmod headers
sudo systemctl restart apache2

設定は VirtualHost に書くのが基本です。.htaccess に書く場合は、そのディレクトリで AllowOverride に FileInfo が含まれている必要があります。WordPress の .htaccess に書くときは、# BEGIN WordPress と # END WordPress の間に書かないでください。この区間は WordPress が書き換えることがあり、追記した内容が消える可能性があります。編集前には設定ファイルのバックアップを取ります。

静的ファイル(フォント)に CORS を設定する

画像やフォントを static.example.com のような別ホストから配信し、本体の https://www.example.com で使う場合の例です。配信する側(static.example.com)の VirtualHost に書きます。

<VirtualHost *:443>
    ServerName static.example.com

    <FilesMatch "\.(woff2?|ttf|otf|eot)$">
        Header set Access-Control-Allow-Origin "https://www.example.com"
        Header merge Vary Origin
    </FilesMatch>
</VirtualHost>

FilesMatch で対象をフォントに絞っているため、他のファイルには影響しません。誰でも使ってよい公開フォントなら * を指定する方法もありますが、自サイト用なら許可するオリジンを書くほうが、他サイトからの流用も防げます。

Vary: Origin は、オリジンによってレスポンスのヘッダーが変わることをキャッシュに伝えるためのものです。CDN やブラウザのキャッシュが、あるオリジン向けのレスポンスを別のオリジンに使い回して CORS エラーになるのを防ぎます。merge を使うと、既存の Vary の値を残したまま追加できます。

複数のオリジンを限定して許可する

Access-Control-Allow-Origin には、オリジンを1つだけか * しか書けません。カンマ区切りで複数並べるとブラウザに拒否されます。複数のオリジンを許可したい場合は、リクエストの Origin ヘッダーを許可リストと照合し、一致したときだけその値を返します。

<IfModule mod_headers.c>
    SetEnvIf Origin "^(https://(www\.)?example\.com|https://app\.example\.net)$" CORS_ORIGIN=$1
    Header always set Access-Control-Allow-Origin "%{CORS_ORIGIN}e" env=CORS_ORIGIN
    Header always merge Vary Origin
</IfModule>

SetEnvIf(mod_setenvif)で Origin が許可リストに一致したときだけ環境変数 CORS_ORIGIN を作り、env=CORS_ORIGIN で、その変数があるときだけヘッダーを付けています。正規表現の先頭の ^ と末尾の $ は省略しないでください。省略すると https://example.com.attacker.example のような別のオリジンにも一致してしまいます。

Origin ヘッダーを照合せずにそのまま返す設定は、* と同じかそれ以上に危険です。特に次の Cookie 付きリクエストを許可している場合は、どのサイトからでもログイン中の利用者の権限でデータを読めることになります。

Cookie 付きのリクエストとプリフライト

認証情報を伴う場合

fetch で credentials: "include" を指定するなど、Cookie や認証ヘッダーを伴うリクエストを許可するには、Access-Control-Allow-Credentials: true が必要です。このとき Access-Control-Allow-Origin に * は使えず、具体的なオリジンを返す必要があります。上の許可リストの方式と組み合わせ、許可するオリジンは自分が管理するものだけにします。

プリフライトリクエストに応える

Content-Type: application/json の POST や、独自のヘッダー(X-WP-Nonce など)を付けたリクエストでは、ブラウザが本番のリクエストの前に OPTIONS メソッドで「このリクエストを送ってよいか」を問い合わせます。これをプリフライトと呼びます。サーバーは許可するメソッドとヘッダーを返す必要があります。

<IfModule mod_headers.c>
    Header always set Access-Control-Allow-Methods "GET, POST, OPTIONS" env=CORS_ORIGIN
    Header always set Access-Control-Allow-Headers "Content-Type, X-WP-Nonce" env=CORS_ORIGIN
    Header always set Access-Control-Max-Age "600" env=CORS_ORIGIN
</IfModule>

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteCond %{ENV:CORS_ORIGIN} !^$
    RewriteRule ^ - [R=204,L]
</IfModule>

許可されたオリジンからの OPTIONS には、mod_rewrite で中身のない 204 を返しています。このように Apache 自身が作るレスポンスにもヘッダーを付けるため、Header には always を指定しています。Access-Control-Max-Age はプリフライトの結果をブラウザがキャッシュする秒数で、ブラウザごとに上限があります。

WordPress の REST API では Apache 側で重ねない

WordPress の REST API(/wp-json/)は、コアの rest_send_cors_headers() によって、リクエストに Origin ヘッダーがあればその値を Access-Control-Allow-Origin として返し、あわせて Access-Control-Allow-Credentials なども送ります。そのため、Apache 側でも /wp-json/ に CORS ヘッダーを付けると、同じヘッダーが2つ届き、ブラウザが「複数の値がある」としてエラーにすることがあります。

REST API で CORS が問題になったときは、まずブラウザの開発者ツールのネットワークタブで、実際に返っているヘッダーを確認してください。WordPress がすでに返しているなら、Apache 側の設定は不要か、/wp-json/ を対象から外す必要があります。許可するオリジンを絞りたい場合は、WordPress 側で rest_pre_serve_request フィルターなどを使ってヘッダーの扱いを変える方法がありますが、コアの挙動はバージョンで変わることがあるため、使っている版のコードとドキュメントを確認してから実装します(2026年10月現在)。

また、Cookie で認証する REST API の書き込み操作には、CORS とは別に nonce(X-WP-Nonce ヘッダー)が必要です。CORS を許可しても、nonce と権限のチェックが省略されるわけではありません。別ドメインのアプリから書き込みを行う構成では、アプリケーションパスワードなど、Cookie に頼らない認証方式も検討してください。

設定を確認する

設定を変えたら、文法を確認して反映し、curl でヘッダーを確かめます。

sudo apache2ctl configtest
sudo systemctl reload apache2

# 通常のリクエスト
curl -sI -H "Origin: https://www.example.com" https://static.example.com/fonts/sample.woff2

# プリフライト
curl -si -X OPTIONS \
  -H "Origin: https://app.example.net" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: content-type" \
  https://example.com/some-endpoint

許可したオリジンでは Access-Control-Allow-Origin が返り、許可していないオリジン(例えば https://other.example)では返らないことを、両方試して確認します。CDN を通している場合は、CDN のキャッシュに古いレスポンスが残っていることがあるため、キャッシュを削除してから確かめてください。

まとめ

  • CORS はブラウザに読み取りを許可する仕組みで、サーバーを守るアクセス制限ではない
  • mod_headers を有効にし、VirtualHost か WordPress の区間外の .htaccess に書く
  • フォントなどは FilesMatch で対象を絞り、Vary: Origin を付ける
  • 複数オリジンは SetEnvIf で許可リストと照合し、^ と $ で前後を固定する
  • Cookie 付きなら * は使えない。プリフライトには always 付きのヘッダーと 204 で応える
  • REST API は WordPress 自身が CORS ヘッダーを出すため、Apache 側で重ねる前に実際のヘッダーを確認する

お気軽にご相談ください

お見積りへ お問い合わせへ
▲