第16章 プロキシサーバー(Squid/Nginxリバースプロキシ)
LPIC-2 208.3〜208.4 相当
前々章ではApacheの基本設定を扱いましたが、ここでは「プロキシ」としての役割に焦点を当て、フォワードプロキシのSquidと、リバースプロキシとしてのNginxを扱います。両者は「クライアントとサーバーの間に立つ」という点は共通していますが、向いている方向が逆であるという違いを意識しながら読み進めてください。
フォワードプロキシとリバースプロキシの違い
フォワードプロキシ(クライアントの代理として外へ出ていく)
[社内クライアントA] \
[社内クライアントB] ---> [Squid(フォワードプロキシ)] ---> [インターネット上の任意のサーバー]
[社内クライアントC] /
リバースプロキシ(サーバーの代理として外から受ける)
[インターネット上の任意のクライアント] ---> [Nginx(リバースプロキシ)] ---> [背後の実サーバー群]
| 種類 | 役割 | 代表例 |
|---|---|---|
| フォワードプロキシ | クライアント側に立ち、社内の複数クライアントの代理としてインターネットへアクセスする(キャッシュ・アクセス制御が主目的) | Squid |
| リバースプロキシ | サーバー側に立ち、外部からのリクエストを代理で受け、背後の実サーバーへ振り分ける(負荷分散・TLS終端が主目的) | Nginx |
Squidの用途
Squidは社内ネットワークの「出口」に置かれる代表的なフォワードプロキシで、主に次の4つの目的で導入されます。
| 目的 | 内容 |
|---|---|
| 帯域節約のキャッシュ | 複数のクライアントが同じコンテンツに繰り返しアクセスする場合、一度取得したコンテンツをキャッシュして再利用し、外部回線の帯域とレスポンス時間を節約する |
| アクセス制御 | 業務に不要なサイトへのアクセスを制限したり、特定の時間帯・部署だけにアクセスを許可したりする |
| ログによる可視化 | 誰が・いつ・どのURLへアクセスしたかを一元的なログとして記録し、監査や利用状況分析に使う |
| 企業ネットワークでの出口集約 | 社内の全クライアントの外部アクセスを1台(または少数台)のプロキシに集約することで、外部から見た通信元を一元化し、監視・制御のポイントを絞り込める |
インストールと基本設定
Squidの設定ファイルは/etc/squid/squid.confです。主要な項目をコメント付きで示します。
/etc/squid/squid.conf の主要部分
http_port 3128 # プロキシが待ち受けるポート番号(既定3128)
cache_dir ufs /var/spool/squid 100 16 256 # キャッシュ領域の定義(下記参照)
cache_mem 256 MB # メモリ上に保持するキャッシュの上限
maximum_object_size 100 MB # これより大きいオブジェクトはキャッシュしない
minimum_object_size 0 KB # これより小さいオブジェクトはキャッシュ対象外にする下限
access_log /var/log/squid/access.log squid # アクセスログの出力先とログフォーマット
cache_log /var/log/squid/cache.log # Squid自体の動作ログ(起動エラー等)の出力先
coredump_dir /var/spool/squid # クラッシュ時のコアダンプ出力先
refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern . 0 20% 4320 # 最終フォールバックパターン(すべてのURLにマッチ)
cache_dir ufs /var/spool/squid 100 16 256の4つの引数は、順に「ストレージ形式(ufsは伝統的なファイルシステム方式)」「キャッシュ保存先のパス」「確保する最大容量(MB単位、この例では100MB)」「第1階層のサブディレクトリ数(16)」「第2階層のサブディレクトリ数(256)」を表します。ディレクトリを2階層に分割するのは、1つのディレクトリに大量のファイルが集中してファイルシステムの性能が劣化するのを防ぐためです。
refresh_patternの書式はrefresh_pattern [-i] regex min percent maxです。minは、この分数の間は無条件に新鮮(サーバーへの再検証なし)とみなす時間、percentはLast-Modified以降の経過時間に対する割合をもとに新鮮さを推定する係数、maxはこれを超えたら必ずオリジンサーバーへ再検証を行う上限時間(分)です。-iを付けると正規表現が大文字小文字を区別しなくなります。複数のrefresh_patternがある場合、上から順に評価され最初にマッチしたものが使われるため、より具体的なパターンを先に、包括的なパターン(.など)を最後に置きます。
$ sudo squid -k parse # 設定ファイルの文法チェック(正常時は無出力、終了コード0)
$ sudo systemctl reload squid # 正常時は無出力
設定ファイルに文法ミスがある場合、-k parseはエラーメッセージを表示して終了コード1を返します。
$ sudo squid -k parse
2026/08/21 09:50:12| FATAL: Bungled squid.conf line 42: htttp_access allow localnet
2026/08/21 09:50:12| Squid Parent: (squid-1) process 4821 exited with status 1
この行のこの値に注目: Bungled squid.conf line 42:のように、エラーになった具体的な行番号と、その行の実際の内容がそのまま表示されます。この例ではhttp_accessがhtttp_accessとタイプミスされており、未知のディレクティブとして解釈できずに起動が拒否されています。reloadを実行する前に必ず-k parseで構文チェックしておかないと、記述ミスのある設定でリロードしてしまい、Squidプロセスがそのまま停止してしまうことがあります。
クライアント側は、ブラウザやOSのプロキシ設定でhttp_portに指定したポートを経由するよう設定します。
ACLの書式
Squidのアクセス制御は、acl <名前> <型> <値>という書式で条件を定義し、http_accessで許可・拒否を決める2段階の仕組みです。よく使う型を一覧にします。
| 型 | 意味 |
|---|---|
src | 送信元のIPアドレス/ネットワーク |
dst | 宛先のIPアドレス |
srcdomain | 送信元の逆引きドメイン名 |
dstdomain | 宛先のドメイン名(前方に.を付けるとサブドメインも含む) |
dstdom_regex | 宛先ドメイン名を正規表現でマッチさせる |
url_regex | URL全体を正規表現でマッチさせる |
urlpath_regex | URLのうちパス部分だけを正規表現でマッチさせる(ドメイン名を含めない) |
port | 宛先のポート番号 |
proto | プロトコル(HTTP、HTTPS、FTPなど) |
method | HTTPメソッド(GET、POST、CONNECTなど) |
time | 曜日・時間帯(例:MTWHF 09:00-18:00。M=月、T=火、W=水、H=木、F=金、A=土、S=日) |
maxconn | 送信元ごとの同時接続数の上限 |
proxy_auth | 認証済みユーザー名(REQUIREDで「何らかの形で認証済みであること」を意味する) |
arp | 送信元のMACアドレス(同一ネットワークセグメント内でのみ有効) |
値を直接列挙する代わりに、外部ファイルから読み込むこともできます。多数のドメインを管理する場合に、squid.conf本体を肥大化させずに済みます。
squid.conf
acl allowed_sites dstdomain "/etc/squid/allowed_sites.txt"
/etc/squid/allowed_sites.txt(1行に1ドメイン)
.example.com
.partner-example.co.jp
http_accessの評価とルールセットの設計
http_accessのルールは、allow/denyを記述された順に上から評価し、最初にマッチした行でその可否が確定します。それ以降のルールは評価されません。
http_accessルールにもマッチしなかった場合、Squidの既定動作は「リスト中の最後のルールの逆」になります。つまり最後のルールがdenyで終わっていれば未マッチのリクエストは許可され、逆に最後がallowで終わっていれば未マッチのリクエストは拒否される、という直感に反する挙動です。この曖昧さを避けるため、ルールセットの末尾には必ず明示的なhttp_access deny allを置き、「それ以外は全て拒否」という意図を明確にしておくのが鉄則です。
実用的なルールセットの完成例を示します。社内ネットワークのみ利用を許可し、業務時間帯のみSNSサイトへのアクセスを禁止し、特定のドメインは常時ブロックする、という3つの要件を組み合わせています。
squid.conf
acl local_net src 192.168.1.0/24
acl blocked_sites dstdomain .example-blocked.com
acl sns_sites dstdomain .facebook.com .twitter.com .instagram.com
acl business_hours time MTWHF 09:00-18:00
http_access deny blocked_sites # 特定ドメインを常時ブロック(時間帯に関わらず)
http_access deny sns_sites business_hours # 業務時間帯のみSNSを禁止(時間外はこのルールにマッチしない)
http_access allow local_net # 社内ネットワークからのアクセスを許可
http_access deny all # それ以外はすべて拒否
この並び順が重要です。blocked_sitesとsns_sitesによる拒否ルールを、包括的なallow local_netより先に置くことで、社内ネットワークからのアクセスであっても、まずブロック対象・SNS制限の判定が優先されるようになっています。http_access deny sns_sites business_hoursは、sns_sitesとbusiness_hoursの両方の条件を満たした場合(AND条件)にのみマッチするため、業務時間外はこのルールにマッチせず、次のルールの評価に進みます。
認証
特定のユーザーだけにインターネットアクセスを許可したい場合、Squidは外部の認証ヘルパープログラムと連携してBasic認証などを行えます。まずLinuxのhtpasswdコマンドでパスワードファイルを用意します。
$ sudo htpasswd -c /etc/squid/passwords alice
New password:
Re-type new password:
Adding password for user alice
この行のこの値に注目:-cは新規にファイルを作成するオプションです。2人目以降のユーザーを追加する際は-cを付けずに実行しないと、既存のファイルが上書きされて1人目のユーザーが消えてしまいます。
squid.conf でのBasic認証設定例
auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/passwords
auth_param basic children 5
auth_param basic realm "Proxy Authentication Required"
auth_param basic credentialsttl 2 hours
acl authenticated_users proxy_auth REQUIRED
http_access allow authenticated_users
http_access deny all
| パラメータ | 意味 |
|---|---|
program | 認証を実際に行うヘルパープログラムの実行ファイルと引数 |
children | 同時に処理できるよう起動しておくヘルパープロセスの数。多いほど同時認証リクエストをさばけるが、メモリ消費が増える |
realm | クライアントの認証ダイアログに表示される文字列 |
credentialsttl | 一度成功した認証情報をキャッシュしておく時間。この間は毎回ヘルパープログラムを呼び出さずに済む |
proxy_auth REQUIREDは「何らかの形で認証済みであること」を条件とするACLです。社内に既にLDAPディレクトリがある場合は、ヘルパープログラムをbasic_ldap_authに差し替えることで、同じproxy_auth REQUIREDの枠組みのままLDAP認証に対応できます。
LDAP認証を使う場合のauth_param(概要)
auth_param basic program /usr/lib/squid/basic_ldap_auth -b "ou=people,dc=example,dc=com" \
-D "cn=admin,dc=example,dc=com" -w adminpassword -h ldap.example.com
この構成にすると、Squidは受け取ったユーザー名・パスワードを毎回LDAPサーバーへ問い合わせて認証を行い、独自のパスワードファイルを二重管理する必要がなくなります。
透過プロキシ(interceptモード)
通常、クライアント側にプロキシのアドレスを明示的に設定する必要がありますが、ネットワーク機器(ルーターやiptables)でクライアントの通信を強制的にSquidへ振り向ける「透過プロキシ」(intercept)という運用もあります。http_port 3128 interceptのように指定し、クライアント側の設定変更なしに全通信をプロキシ経由にできます。
キャッシュの動作確認
Squidのアクセスログは既定で/var/log/squid/access.logに記録されます。
$ tail -f /var/log/squid/access.log
1692500000.123 45 192.168.1.10 TCP_HIT/200 5432 GET http://example.com/style.css - HIER_NONE/- text/css
1692500001.456 210 192.168.1.11 TCP_MISS/200 8210 GET http://example.com/app.js - DIRECT/93.184.216.34 application/javascript
1692500002.789 38 192.168.1.10 TCP_REFRESH_HIT/304 312 GET http://example.com/style.css - DIRECT/93.184.216.34 text/css
| 表記 | 意味 |
|---|---|
TCP_HIT | キャッシュにある新鮮なコピーをそのまま応答できた |
TCP_MISS | キャッシュになく、オリジンサーバーへ取得しに行った |
TCP_REFRESH_HIT | キャッシュにあったが鮮度が切れていたため再検証(条件付きGET)を行い、内容に変更がなかった(304応答)ためキャッシュから応答した。オリジンへの再検証は発生したが、本体データの再送信は節約できている |
アクセスログの各フィールドの構成は次の通りです。
| 順序 | フィールド | 意味 |
|---|---|---|
| 1 | タイムスタンプ | Unixエポック秒(小数部はミリ秒) |
| 2 | 応答時間 | ミリ秒単位でのリクエスト処理時間 |
| 3 | クライアントIPアドレス | リクエスト元のIPアドレス |
| 4 | 結果コード | TCP_HIT/200のように、キャッシュ結果とHTTPステータスコードの組み合わせ |
| 5 | サイズ | 応答本体のバイト数 |
| 6 | メソッド | GET/POSTなどのHTTPメソッド |
| 7 | URL | リクエストされたURL |
| 8 | ユーザー名 | 認証済みの場合はユーザー名、未認証は- |
| 9 | 階層コード/取得先 | DIRECT/IPアドレスのように、オリジンへ直接取得しに行ったか、親キャッシュ経由かなどを示す |
| 10 | コンテンツタイプ | 応答のMIMEタイプ |
$ squidclient mgr:info
Squid Object Cache: Version 5.7
...
Number of clients accessing cache: 12
Hit Ratio (5 min): 42.3%
Hit Ratio (60 min): 38.1%
この行のこの値に注目:squidclient mgr:infoで表示されるHit Ratioが低い場合、cache_dirで確保しているキャッシュ容量が不足している、あるいはキャッシュ対象外のコンテンツ(動的生成ページなど)が多いといった原因が考えられます。
クライアント側の設定
クライアントやパッケージ管理ツールがプロキシ経由で通信するための設定方法は、環境によって異なります。
シェルの環境変数(多くのCLIツールが参照する)(いずれも正常時は無出力)
$ export http_proxy="http://proxy.example.com:3128"
$ export https_proxy="http://proxy.example.com:3128"
$ export no_proxy="localhost,127.0.0.1,.example.com"
no_proxyは、プロキシを経由せず直接アクセスしたい宛先を指定します。社内向けのサーバーまでプロキシ経由にしてしまうと、到達できなくなったり不要な負荷がかかったりするため、忘れずに設定します。
apt(Debian系):/etc/apt/apt.conf.d/95proxy
Acquire::http::Proxy "http://proxy.example.com:3128";
Acquire::https::Proxy "http://proxy.example.com:3128";
yum/dnf(RHEL系):/etc/yum.conf または /etc/dnf/dnf.conf
proxy=http://proxy.example.com:3128
squidコマンドによる運用操作
| コマンド | 用途 |
|---|---|
squid -k parse | 設定ファイルの構文チェックのみを行う(プロセスは起動しない) |
squid -k reconfigure | 稼働中のプロセスに設定の再読み込みを指示する。既存の接続を切断せずに新しい設定を反映できる |
squid -z | cache_dirで定義したキャッシュディレクトリの構造を初期化する。初回構築時や、cache_dirの設定(パスや容量)を変更した際に必要 |
$ sudo squid -z
Creating Swap Directories
$ sudo systemctl start squid # 正常時は無出力
squid -zを既存のキャッシュディレクトリに対して実行すると、そのディレクトリ構造が再作成されます。稼働中のSquidと同時に実行するとキャッシュの不整合を招く可能性があるため、初回構築時か、Squidを停止した状態で実行してください。
トラブルシューティング
クライアントから403 Forbiddenが返る場合、原因の多くはACLとhttp_accessの組み合わせにあります。切り分けの基本は、上から順に評価されるルールのどこで拒否が確定しているかを追うことです。
- 該当クライアントのIPアドレス・アクセス時刻・アクセス先URLを確認する
- squid.confの
http_access行を上から順に読み、その条件(src、dstdomain、timeなど)にクライアントの状況が一致するかを1行ずつ確認する - 途中に
denyの行があり、そこにマッチしていれば、それ以降のallow行は評価されないため、そこが拒否の原因になる
加えて、Squid自体の起動エラーや内部的な異常は、アクセスログ(access.log)ではなくcache.logに記録されます。設定変更後にSquidが起動しない場合は、まずcache.logを確認します。
$ sudo tail -50 /var/log/squid/cache.log
2026/08/21 10:00:01 kid1| Set Current Directory to /var/spool/squid
2026/08/21 10:00:01 kid1| FATAL: Bungled squid.conf line 42: unrecognized directive
この行のこの値に注目:FATALで始まる行が、起動失敗の直接の原因です。この例では設定ファイルの42行目に認識できないディレクティブがあることを示しており、typoやディレクティブ名の誤りが典型的な原因です。
Nginxのサーバーブロック
/etc/nginx/sites-available/example.com
server {
listen 80;
server_name example.com;
root /var/www/example.com;
location / {
try_files $uri $uri/ =404;
}
}
$ sudo nginx -t # 設定ファイルの文法チェック
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl reload nginx # 正常時は無出力
設定に文法ミスがある場合、nginx -tはエラー箇所のファイル名・行番号を示して終了コード1を返します。
$ sudo nginx -t
nginx: [emerg] unexpected "}" in /etc/nginx/sites-enabled/example.com:9
nginx: configuration file /etc/nginx/nginx.conf test failed
この行のこの値に注目: syntax is okとtest is successfulの2行がどちらも表示されて初めて設定が正常です(構文だけは正しくても、参照しているファイルが存在しないなど別の理由でtest failedになることもあります)。エラー時は[emerg]に続けてファイル名と行番号(この例ではexample.com:9)が具体的に示されるため、該当箇所をすぐに特定できます。reloadの前に必ず-tで確認する習慣をつけておくと、設定ミスのままリロードしてNginxが停止する事故を防げます。
Nginxのlocationブロックは複数定義でき、どれにマッチするかには優先順位があります。完全一致(location = /path)が最優先、次に正規表現一致(location ~やlocation ~*)、最後に前方一致(プレフィックス一致)という順で評価されます。複数のlocationブロックを組み合わせる際は、この優先順位を意識しないと意図しないブロックがマッチしてしまうことがあります。
sites-availableに設定ファイルを置いただけでは有効になりません。Debian系ではa2ensiteでsites-enabledにシンボリックリンクを作成する必要があります(Nginxも同様の構成を取ることが多いですが、Nginx自体にはa2ensiteに相当する公式コマンドはなく、ln -sで手動リンクするのが一般的です)。設定ファイルを編集したのに反映されない場合は、まずこのリンクの有無を確認しましょう。
Nginxをリバースプロキシとして使う
Nginxは静的コンテンツの配信だけでなく、背後にあるアプリケーションサーバー(Node.js、Gunicornなど)へリクエストを中継する「リバースプロキシ」としても広く使われます。TLS終端やロードバランシングを前段でまとめて担わせる構成が一般的です。
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
バックエンドが複数台ある場合はupstreamブロックで振り分け先をまとめて定義し、簡易的なロードバランシングも行えます。
upstream app_servers {
server 127.0.0.1:8081;
server 127.0.0.1:8082;
}
server {
location / {
proxy_pass http://app_servers;
}
}
フォワードプロキシのSquidが「クライアントの代理」であるのに対し、リバースプロキシのNginxは「サーバーの代理」である、という向きの違いを意識しておくと、両者を混同せずに使い分けられます。実務では、外部公開用にはNginxのリバースプロキシ構成、社内クライアントの出口対策にはSquidのフォワードプロキシ構成、というように役割ごとに使い分けるのが一般的です。