nginxで複数サーバーのウェブページを1つのオリジンにリバースプロキシする

# この記事について


複数オリジンにデプロイされているウェブサイトをnginxでリバースプロキシして1つのオリジンにまとめて公開する手順をまとめます


# やりたいこと


バックエンドにRESTful API、フロントエンドをSPAで構成するウェブアプリに、セッション認証によるログイン機能を実装したい


セッション認証なので、CSRFを防ぎやすいように1つのオリジンで公開したい
(別オリジンからのCSRFトークン取得を許可したくない)


同一サーバーでポートを分けてデプロイする場合も、別サーバーに分けてデプロイする場合も、デプロイ時点でのオリジンは別になりますので、単体でデプロイした時点では同一オリジンが実現できませんが、リバースプロキシで1つのオリジンにまとめることが可能ですのでこれをやりたい


API＋SPAのセッション認証については以下参照
https://qiita.com/studio_haneya/items/d4891d97fc9eb5f6e7bf


# 方法


リバースプロキシとしてはnginxの採用例が多いようです


今回の用途ではAPI、SPAともにApache2(httpd)で公開する想定でしたので、機能の似たものが重複して無駄に負荷が増えるんじゃないかと思ったんですが、動的サーバーはApacheの方が得意な一方で、トラフィックをそのままさばくリバースプロキシのような用途ではnginxの方が得意なので、併用するのも別におかしくはないようです


リバースプロキシの実装自体もnginxの方が簡単なようですので、今回はnginxでリバースプロキシすることにしました


# 手順


以下の手順でやっていきます


1. サンプルページをつくる
2. docker-compose.ymlを作成する
3. nginx.confにリバースプロキシ設定を書く


今回はdockerで試作していきます


ディレクトリ構造は以下のようにしました


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/f49acf84-c00b-4044-ad66-302b57263fce.png" width=30%>


完成したコードは以下にあります
https://github.com/haneya-studio/nginx_reverse_proxy


## 1. サンプルページをつくる


以下のように適当なサンプルページを作成しました


```html:/app1/index.html
<h1>THIS IS /app1/index.html</h1>
<div><a href="/">/app1/index.html</a></div>
<div><a href="/hoge/">/app1/hoge/index.html</a></div>
<div><a href="/fuga/">/app1/fuga/index.html</a></div>
<div><a href="/app2/">/app2/index.html</a></div>
<div><a href="/app2/hoge/">/app2/hoge/index.html</a></div>
<div><a href="/app2/fuga/">/app2/fuga/index.html</a></div>
```


1行目だけ変えていまどのファイルが表示されているか分かるようにしています


```html:/app1/hoge/index.html
<h1>THIS IS /app1/hoge/index.html</h1>
<div><a href="/">/app1/index.html</a></div>
<div><a href="/hoge/">/app1/hoge/index.html</a></div>
<div><a href="/fuga/">/app1/fuga/index.html</a></div>
<div><a href="/app2/">/app2/index.html</a></div>
<div><a href="/app2/hoge/">/app2/hoge/index.html</a></div>
<div><a href="/app2/fuga/">/app2/fuga/index.html</a></div>
```


## 2. docker-compose.ymlを作成する


nginxの挙動が分かれば良いので、app1, app2をそれぞれhttpdで表示してポート8000, ポート8080に表示、nginxをポート80適当にそれぞれのファイルをコンテナで表示するようにしました


ポート80に両方とも表示できるようにしているので、ポート8000とポート8080の表示は不要ですが、動作確認の為にポートマッピングをつけています


```yml:docker-compose.yml
services:
  app1:
    image: httpd:latest
    ports:
      - "8000:80"
    volumes:
      - ./app1:/usr/local/apache2/htdocs/
    restart: always 

  app2:
    image: httpd:latest
    ports:
      - "8080:80"
    volumes:
      - ./app2:/usr/local/apache2/htdocs/
    restart: always 

  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app1
      - app2
    restart: always
```


## 3. nginx.confにリバースプロキシ設定を書く


nginxを使って
/ にapp1のlocalhost
/app2/ にapp2のlocalhost
を表示します


```nginx:/nginx/nginx.conf
events {
}

http {
    server {
        listen 80;
        server_name localhost;

        location / {
            proxy_pass http://app1;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        location /app2/ {
            proxy_pass http://app2;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
} 
```


## 4. 動作確認


```terminal:terminal
docker compose up
```


以下に表示されている筈です
http://localhost


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/cc09517c-e50b-42ae-aad8-2523cbe0c078.png" width=70%>


適当にリンクをクリックしながらURLを確認すると挙動が確認できます


上記以外にも以下にそれぞれのコンテナ自身が公開しているページが確認できます
http://localhost:8000
http://localhost:8080


## まとめ


普通に静的サイトをリバースプロキシするだけならとても簡単です


Djangoなどの動的サイトをリバースプロキシする場合は公開オリジンが変わってDjangoのCORSやALLOWED_HOSTの設定、ApacheのServerAlias設定などで引っかかったりしますので、表示できなくなったら一度全部ガバガバ設定にしてから適切に設定しなおすのが良いと思います


レッツトライ