はじめに
- Next.jsでフロントエンドを作成して、そのドメインを
web-bonsai.tech
としています。 - RailsでAPIを作成して、そのドメインを
api.web-bonsai.tech
としています。
APIはweb-bonsai.techからのリクエストだけ受け付けたいので、リクエストヘッダーとリファラをチェックするようにしたときのメモです。
nginx.confはGitHubのリポジトリにはコミットされないように、.gitignoreしています。
nginx.confを編集する
以下のようにしてみました。
server { 〜略〜 # invalidなリクエストかどうか判定するための変数を定義 set $invalid_request ''; # $invalid_refererのとき$invalid_requestを1にする valid_referers server_names example.com; if ($invalid_referer) { set $invalid_request 1; } # $invalid_refererだったとしても、headers['X-API-PASSWORD']が一致したとき$invalid_request = ''に戻す if ($http_x_api_password = 'abcde') { set $invalid_request ''; } if ($invalid_request) { return 403; } 〜略〜 }
nginx.confのコードについての説明
まず判定用の変数$invalid_request
を定義して、空文字列を入れておきます。
# invalidなリクエストかどうか判定するための変数を定義 set $invalid_request '';
リファラが無い場合や、リファラのサーバ名がexample.com
でない場合は、以下のブロックで変数$invalid_request
が1になります。
# $invalid_refererのとき$invalid_requestを1にする valid_referers server_names example.com; if ($invalid_referer) { set $invalid_request 1; }
Next.jsでSSGビルドをするとき、getStaticProps()内でfetch()すると、リファラが無いので上記のブロックで変数$invalid_request
が1になってしまいますが、リクエストヘッダーに正しい X-API-PASSWORD
が存在する場合は、変数$invalid_request
を空文字列に戻します。
ここでは 'abcde'
と比較していますが、実際には文字数や文字種の多いパスワードと比較します。
# $invalid_refererだったとしても、headers['X-API-PASSWORD']が一致したとき$invalid_request = ''に戻す if ($http_x_api_password = 'abcde') { set $invalid_request ''; }
$invalid_requestが空文字列でない場合は403を返します。
if ($invalid_request) { return 403; }
Next.jsのgetStaticProps()の記述内容
以下の通り、headersを設定して送信すると、nginx.conf内では変数$http_x_api_password
で参照できます。
export const getStaticProps = async () => { const response = await fetch(`${process.env.API_BASE_PATH}/api/tasks`, { headers: { 'X-API-PASSWORD': process.env.X_API_PASSWORD ?? '', }, }); const props = await response.json(); return { props, }; };
環境変数の定義
frontend/.env.local
に以下の通り定義します。
X_API_PASSWORD=abcde
frontendサービスでyarn buildする
いつも通り以下のコマンドでビルドします。
$ docker-compose run --rm frontend yarn build
おわりに
nginx.conf内で2つの変数の文字列を結合して判定したり、正規表現を使って判定するともっと適切な書き方がありそうな気がします。
また、web-bonsai.techのフォーム入力などからリクエストする場合には、httponlyでsecureなcookieなどを使って判定した方がよさそうだなと思っていますが、nginx.confに慣れていなくてつらいので今のところはここまでにしておきます。