この記事はプログラミング学習者が学習した内容を備忘録として記事におこしたものです。内容に不備などあればご指摘頂けると助かります。
前提条件
この記事はアプリケーションを制作中に開発環境でフロントエンドとバックエンドを通信できるように設定した時の実装内容を紹介しています。
使用したPC
MacBookAir M2のCPU
使用した主要なgemなど
バックエンド
Ruby: 3.4.1.0-slim-bookworm(Dockerイメージ)
Rails: 8.1.3.1
フロントエンド
Node.js: 24.21.0-bookworm-slim(Dockerイメージ)
TypeScript: 5.9.3
React: 19.2.8
Next.js: 16.3.6
データベース
PostgreSQL: 18.6-bookworm(Dockerイメージ)
実装概要
開発環境において、フロントエンドとバックエンドが疎通できるように設定を加えていてきます。
先ず、実際のdocker-compose.ymlを先に掲載しておきます。
services:
db:
image: postgres:18.6-bookworm
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: treasure_box_development
volumes:
- db:/var/lib/postgresql
ports:
- '127.0.0.1:5432:5432'
networks:
- backend_net
valkey:
image: valkey/valkey:9.1.2
# サーバー起動コマンド 60秒に1回以上変更があれば永続化保存(コンテナ停止時にメモリが消えるため) ログを推奨程度で表示
command: valkey-server --save 60 1 --loglevel notice
volumes:
- valkey-data:/data
healthcheck:
test: ['CMD', 'valkey-cli', 'ping']
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
ports:
- '127.0.0.1:6379:6379'
networks:
- backend_net # 内部DNS(名前解決)でIPアドレスが変わってもOK 他のDockerプロジェクトから分離
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
target: development
command: npm run dev
volumes:
- ./frontend:/app # バインドマウント(ホストPCと接続)
- /app/node_modules # 匿名ボリューム(ホストPCと同期せず、Docker内部に別途保存)
- /app/.next # 匿名ボリューム(ホストPCと同期せず、Docker内部に別途保存)
ports:
- '127.0.0.1:8000:3000' # ホスト側ポート(左側)がbackendと重複しないように。コンテナ側は他コンテナと独立しているので重複問題無し
tty: true
stdin_open: true
networks:
- frontend_net
backend:
build:
context: ./backend
dockerfile: Dockerfile
target: development
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
environment:
DATABASE_HOST: db
DATABASE_USER: postgres
DATABASE_PASSWORD: password
VALKEY_URL: redis://valkey:6379/0
networks:
- frontend_net
- backend_net
volumes:
- ./backend:/app
- bundle:/usr/local/bundle
ports:
- '127.0.0.1:3000:3000' # ホスト側ポート(左側)がfrontendと重複しないように。コンテナ側は他コンテナと独立しているので重複問題無し
depends_on:
db:
condition: service_started
valkey:
condition: service_healthy
tty: true
stdin_open: true
volumes:
bundle:
db:
valkey-data:
networks:
frontend_net:
driver: bridge # 同一ネットワーク内でのみ通信可
backend_net:
driver: bridge # 同一ネットワーク内でのみ通信可
通常、ブラウザを使った通信には同一オリジンポリシー(SOP)が原則として当てはまり、同じオリジン同士でしか通信ができないように設定されています。
ここで、オリジンについて説明しておきます。
https://www.sample.com/443/questions/1 というURLを例として説明すると、
以下のように分類して説明することができます。
https: スキーム(プロトコル)
www: ホスト
sample.com: ドメイン
443: ポート
questions/1: パス
オリジンとは、スキーム、ホスト、ドメインとポートまでを範囲とします。
上記の例ではhttps://www.sample.com/443 となります。
URLでポートが表示されるずに省略されるので、実質はhttps://www.sample.com と同義です。
現状の開発環境(上述のdocker-compose.yml)ではフロントエンドは127.0.0.1(loalhost):8000、バックエンドは127.0.0.1(localhost):3000なので、同一オリジンポリシー(SOP)によりフロントエンドからバックエンドのアクセスはブロックされてしまいます。
そこでCORSという異なるオリジン間でリソースの共有ができるようになる仕組みを利用することになります。
以下はCORSによる接続までの設定を解説しています。
先ず、バックエンド側のRubyでgem "rack-cors"をインストールします。
通常はGemfileの中でコメントアウトされているので解除して使いましょう。
gem "rack-cors"
# ローカル
bundle install
# Docker内部(上述のymlファイルのサービス使用時)
docker compose run --rm --no-deps backend bundle install
bundle install: Gemfileに新しく加えられてGemをインストールする
docker compose run: 既存のイメージを使ってコンテナを作成・起動、イメージがなければbuildする
--rm: 作ったコンテナをコマンドの実行が終わったら削除する
--no-deps: docker-compose.ymlで設定されたdepends_onのサービスは起動しない
backend: docker-compose.ymlで指定されたサービス名
次にconfig/initializers/cors.rbでコメントアウトされているコードを解除後に追記して使います。
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins "http://localhost:8000" # 許可するフロントエンドのURL
resource "*",
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
# 許可するHTTPメソッドの一覧
credentials: true # Cookie共有の許可を決める(true/false)
end
end
originsは許可するフロントエンド側のオリジンを指しています。
最後にconfig/environments/development.rbに追記します。
Rails.application.configure do
config.hosts << "backend" # Railsのサービス名を入力
end
backendの部分はdocker-compose.yml上のサービス名を記述しています。
Docker compose内の内部ネットワークではDNSがサービス名をドメイン名として解釈してくれます。
この設定はRails6以降から導入されたHost Authorizationという機能設定で、Host名を検証することでHTTPホストヘッダー攻撃を防ぎます。
HTTPホストヘッダーとはバーチャルホストの仕組みでは一つのIPアドレスに複数のドメインを設定されたWebサイトにアクセスがきた時にどのドメインへのアクセスなのかを判別するためにクライアント側が送信するために必要な要素です。
HTTPホストヘッダーの中身はホスト名(ドメイン名)とポート名で構成されています。
HTTPホストヘッダー攻撃とはHostヘッダーが信用されたり、不適切に設定されるとアプリケーションに脆弱性が生まれる可能性があり、攻撃者がHostヘッダーに様々なコンテンツを入れることで件の脆弱性につけ入り、様々な脆弱性を引き起こすことを指します。
以上で、CORSの設定が完了して、フロントエンドとバックエンドの疎通が成立するようになります。
最後まで読んでいただきありがとうございます。
参考にしたサイト
オリジン間リソース共有 (CORS)とは
なんとなく CORS がわかる...はもう終わりにする。
同一オリジンポリシー
HTTP Host ヘッダー攻撃とは?