Forward Proxy vs Reverse Proxy

バックエンド開発の経験があれば、「フォワードプロキシ」と「リバースプロキシ」という用語をどこかで聞いたことがあるかもしれません。

そのため、今日はそれらについて「概要と用途」を詳しく説明させていただきます。

## フォワードプロキシ (Forward Proxy)

フォワードプロキシまたは単純なプロキシは、**クライアント**と**インターネット**の間にある仲介者です。
ざっくりにはこんな感じです。

![Without Proxy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/78046df1-43cc-b90e-e49e-10fe065184dc.png)

サーバーがある場合

![With Server & Proxy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/ad79e0c9-a6fc-ddbe-5fd4-fff5d95eb4fa.png)

そして、サーバーに直接接続する代わりに、クライアントはプロキシ経由でサーバーに接続し、クライアントに代わってプロキシがサーバーとやりとりするということになります。

さて、「プロキシの目的は何ですか?」と自問があるでしょうか。

簡単に言うと、それらは次となります。
① facebook.com や 18+ サイトなどの特定のサイトへのアクセスを制限する。
② クライアントがサイト全体ではなく、一部のみにアクセスできるようにする (リソースアクセスを制限する)。
③ インターネットでの活動を記録する。
...

## リバースプロキシ (Reverse Proxy)

リバースプロキシは、**インターネット**と**サーバー**の間にある仲介者です。
一旦こんな感じです。

![Reverse Proxy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/984e241d-ad02-33c8-c24b-9b9fec63e2e5.png)

では、リバースプロキシの目的は何でしょうか。

これはサーバーを表します。つまり、「リバースプロキシ」はクライアントからのリクエストを受け取り、それをサーバーに送信し、サーバーからのレスポンスを受け取り、それをクライアントに送信します。下記の画像はイメージです。

![Req.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/01d100f1-659b-5852-ccbc-b5b01ef7b663.png)

「リバースプロキシ」はサーバーの代表であるため、**クライアントはサーバーと通信していることを認識せず**、**リバースプロキシと通信していることを認識しているだけです**。

じゃ、リバースプロキシの目的とはどのようなものでしょうか。
まあ、それは次のようにリストすることができます。
① サーバーを公開したくない場合、「リバースプロキシ」は、クライアントからサーバーを非表示にするための最良の選択です。
② 「リバースプロキシ」はサーバーの前にあるため、ロードバランシングとして機能して、システムに複数のサーバーがある場合クライアントからサーバーにリクエストを均等に分散できます。
...

コンセプトは十分なので、デモを見てみましょう。 まず、フォワードプロキシで確認します。 ここでは、次のように「フォワードプロキシ」経由で`http://google.com`に接続するクライアントを作成します。

![CLIENT UMM_PROXY wwwwwygoogle.com (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/02ebc152-d673-c002-0f4b-61942266e794.png)

JavaScriptライブラリ`request`を使用してプロキシ経由で`http://google.com`にリクエストを送信するクライアントをセットアップします。

※ ライブラリのリンク先: https://www.npmjs.com/package/request

```js
// index.js
const request =  require('request');

request({
  url: 'http://www.google.com/',
  method: 'GET',
  proxy: 'http://127.0.0.1:8888',
}, (err, response, body) => {
  if (!err && response.statusCode === 200) {
    console.log(body);
  }
});
```

nginx を次のように設定します。

```nginx
events {}

http {
  server {
    listen 8888 default_server;

    access_log  log/access.log; #アクセスログ
    error_log log/error.log debug; #エラーログ

    location / {
      resolver 8.8.8.8;
      proxy_pass https://$host$request_uri;
    }
  }
}
```

上のnginx設定の意味はnginxがローカルホストのポート**8888**でリッスンして、リクエストが`http://localhost:8888/`に来るたびに、リクエストは`https://$host$request_uri`に転送されます。ここでは

- `$host`は、リクエストヘッダー内のホストの名前です (今回は`goole.com`です)。
- `$request_uri`はホスト名の後の**URI**です。(例:`google.com/status`の場合、`$request_uri`は`/status`です)

まず、クライアントを起動する

```bash
node index.js
```

次にnginxのログを確認してみましょう。

![Screen Shot 2022-12-11 at 18.34.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/63af1913-309f-dd6e-678f-d95d0b66548b.png)

プロキシがクライアントに代わってリクエストを`google.com`に送信することがわかります。 プロキシは`google.com`からの応答を受け取り、それをクライアントに送り返します。

![Screen Shot 2022-12-11 at 18.35.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/2287f9a9-c760-9285-7540-5860b5dec920.png)

フォワードプロキシについてはここまで以上です。

**リバースプロキシ**なんですが、次のように、それぞれ`expressJS`によってポート`1111`、`2222`、および`3333`でリッスンする3つのサーバーを作っておきます。

**サーバー1** (ポート**1111**でリッスンする)

```js
const express = require('express');

const app = express();

app.get('/', (req, res) => {
  res.send('Server 1\n');
});

app.listen(1111, () => {
  console.log('Listening on port 1111');
});
```

**サーバー2** (ポート**2222**でリッスンする)

```js
const express = require('express');

const app = express();

app.get('/', (req, res) => {
  res.send('Server 2\n');
});

app.listen(2222, () => {
  console.log('Listening on port 2222');
});
```

**サーバー3** (ポート**3333**でリッスンする)

```js
const express = require('express');

const app = express();

app.get('/', (req, res) => {
  res.send('Server 3\n');
});

app.listen(3333, () => {
  console.log('Listening on port 3333');
});
```

そして、nginxの設定は次のとおりです。

```nginx
events {}

http {
  upstream express_servers {
    server localhost:1111;
    server localhost:2222;
    server localhost:3333;
  }

  server {
    listen 8888;

    location / {
      proxy_pass http://express_servers;
    }
  }
}
```
nginxの設定について説明します。ここで、nginxはローカルホストのポート**8888**でリッスンし、`http://localhost:8888/`へのすべてのリクエストは、`express_servers`リスト (**Server1**、**Server2**、**Server3**)の3つのサーバーのいずれかに転送されます。 

ここで使用されるデフォルトのリクエストローテーションアルゴリズムは**ラウンドロビン**です。

これは、各サーバーに対応するサーバーの重みに基づいて、サーバーへのリクエストをローテーションするアルゴリズムです。 重みが高いほど、リクエストを受信する頻度が高くなり、その逆も同様です。 デフォルトでは、全サーバーの重みは同じです。

「リバースプロキシ」が意図したとおりに動作するかどうかを確認するには、下記のコマンドを使ってみてください。

```bash
while sleep1; do curl http://localhost:8888; done
```

上記のコマンドは定期的に`curl http://localhost:8888`コマンドをリバースプロキシに対して毎秒実行します。下は我々の結果なんです。

![191910278-62a2fe09-8940-49d1-9d0e-c660d375e2d0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135669/53a50e03-0a92-1726-3a8c-dc1d2a8e5d16.png)

リクエストはそれぞれ Server1、Server2、および Server3にルーティングされています。

リバースプロキシについては以上です。 

読んでくれてありがとうございます。次のブログにお会いしましょう。