2026年5月17日
2026年5月17日
NginxのTCP/UDPプロキシ(streamモジュール)設定方法|MySQL・DNSの例
Nginxは、HTTPだけでなく、TCPやUDPの通信をそのまま別のサーバーへ中継(プロキシ)することもできます。この機能を提供するのが stream モジュールです。MySQL・MariaDBの接続を複数のデータベースサーバーへ振り分けたり、DNSの問い合わせを複数のDNSサーバーへ分散したりできます。この記事では、モジュールの確認、設定ファイルの置き場所、TCPとUDPの設定例、ログとアクセス制限を説明します。内容は2026年10月時点のNginx(安定版)とUbuntu・Debian・RHEL系のパッケージを前提にしています。
手順1:streamモジュールが使えるか確認する
stream モジュールは、ソースからビルドする場合は既定で組み込まれません。インストール済みのNginxで使えるかを確認します。
nginx -V 2>&1 | tr ' ' '\n' | grep stream
--with-stream または --with-stream=dynamic が表示されれば使えます。=dynamic の場合は、モジュールが別パッケージになっていることが多いので、インストールします。
# Ubuntu / Debian(ディストリビューションの nginx パッケージ)
sudo apt install libnginx-mod-stream
# RHEL / AlmaLinux / Rocky Linux
sudo dnf install nginx-mod-stream
これらのパッケージは、インストールするとモジュールを読み込む設定(load_module)も自動で追加します。nginx.org が配布する公式パッケージは、stream モジュールが最初から組み込まれています。
手順2:streamブロックの置き場所を決める
stream ブロックは、http ブロックと同じ階層(nginx.conf の一番外側)に書きます。http ブロックの中には書けません。
多くのパッケージでは、/etc/nginx/conf.d/*.conf や /etc/nginx/sites-enabled/* は http ブロックの中で読み込まれています。そこに stream の設定ファイルを置くとエラーになるため、stream 用のディレクトリを別に作り、nginx.conf の一番外側で読み込みます。
# /etc/nginx/nginx.conf(抜粋)
events {
worker_connections 1024;
}
http {
# ... 既存の Web サーバーの設定(変更しない) ...
include /etc/nginx/conf.d/*.conf;
}
# http ブロックの外に追加する
stream {
log_format stream_basic '$remote_addr [$time_local] $protocol $status '
'$bytes_sent $bytes_received $session_time '
'"$upstream_addr" $upstream_connect_time';
access_log /var/log/nginx/stream-access.log stream_basic;
include /etc/nginx/stream.d/*.conf;
}
sudo mkdir -p /etc/nginx/stream.d
HTTPの server ブロック(server_name や location を持つもの)は、従来どおり http ブロックの中に書きます。stream の中の server ブロックはTCP/UDP用で、server_name や location は使えません。
log_format は http と stream で別々に定義します。stream で使える主な変数は次のとおりです。
| 変数 | 内容 |
|---|---|
$remote_addr |
接続元のIPアドレス |
$protocol |
TCP または UDP |
$status |
セッションの結果(200 は正常終了、502 はバックエンドに接続できなかったなど) |
$bytes_sent / $bytes_received |
クライアントへ送った・クライアントから受け取ったバイト数 |
$session_time |
セッションの長さ(秒) |
$upstream_addr |
中継先のサーバー |
$upstream_connect_time |
中継先への接続にかかった時間 |
手順3:TCPのプロキシを設定する(MySQLの例)
WordPressのサーバーから、MySQLへの接続をNginx経由にし、主サーバーが落ちたら予備のサーバーへ切り替える例です。
# /etc/nginx/stream.d/mysql.conf
upstream mysql_backend {
server 192.168.1.21:3306 max_fails=3 fail_timeout=30s;
server 192.168.1.22:3306 backup;
}
server {
listen 3306;
proxy_pass mysql_backend;
proxy_connect_timeout 5s;
proxy_timeout 10m;
# 接続を許可する範囲を絞る
allow 192.168.1.0/24;
deny all;
}
listen 3306;のように、ポート番号だけを書くとTCPで待ち受けます。TCPを表すtcpというパラメーターはありません。listen 3306 tcp;と書くとinvalid parameter "tcp"のエラーになりますmax_fails=3 fail_timeout=30sは、30秒の間に3回接続に失敗したら、そのサーバーを30秒間使わない設定ですbackupを付けたサーバーは、ほかのサーバーがすべて使えないときだけ使われますproxy_connect_timeoutはバックエンドへの接続を待つ時間(既定60秒)、proxy_timeoutは通信が途絶えてから接続を閉じるまでの時間(既定10分)です。データベースのように長く接続を保つ用途では、短くしすぎると切断が起きますallowとdenyで、接続できる送信元を絞れます。データベースのポートをインターネットに公開しないよう、送信元の範囲を限定してください
Nginx(オープンソース版)の振り分け先の監視は、実際の接続の失敗で判断する受動的なものだけです。定期的に接続を試す能動的なヘルスチェックは、商用版のNGINX Plusの機能です。
なお、MySQLのレプリケーション構成では、書き込みは主サーバーにしかできません。読み取り専用のレプリカへ書き込みを振り分けないよう、用途に合わせて構成してください。
手順4:UDPのプロキシを設定する(DNSの例)
UDPで待ち受けるには、listen に udp を付けます。DNSは大きな応答をTCPで返すことがあるため、TCPとUDPの両方を待ち受けます。
# /etc/nginx/stream.d/dns.conf
upstream dns_backend {
server 192.168.1.31:53;
server 192.168.1.32:53;
}
# UDP の 53 番
server {
listen 53 udp;
proxy_pass dns_backend;
proxy_responses 1;
proxy_timeout 5s;
}
# TCP の 53 番
server {
listen 53;
proxy_pass dns_backend;
proxy_timeout 30s;
}
UDPには接続の終わりがないため、Nginxは応答を待つ時間や数で終わりを判断します。proxy_responses 1; は「1つの問い合わせに対して応答は1つ」という指定で、応答を受け取った時点でセッションを終えます。これがないと、proxy_timeout の間セッションが残り続け、問い合わせが多いとリソースを消費します。
手順5:送信元IPアドレスをバックエンドに伝える
stream のプロキシでは、バックエンドから見た接続元はNginxのIPアドレスになります。メールサーバーのように送信元IPアドレスで動作を変えるサービスでは問題になります。バックエンドがPROXYプロトコルに対応していれば、proxy_protocol on; で元の接続元を伝えられます。
# /etc/nginx/stream.d/smtp.conf
upstream smtp_backend {
server 192.168.1.41:25;
}
server {
listen 25;
proxy_pass smtp_backend;
proxy_protocol on;
}
バックエンド側でPROXYプロトコルの受け入れを有効にしていないと、接続が正しく処理されません。たとえばPostfixでは、受け入れ側の設定が別に必要です。対応していないサービスには proxy_protocol on; を付けないでください。
手順6:設定を反映して確認する
# 構文を確認
sudo nginx -t
# 接続を切らずに設定を読み込み直す
sudo systemctl reload nginx
# 待ち受けているポートを確認(TCP と UDP)
sudo ss -lntup | grep nginx
nginx -t で "stream" directive is not allowed here と出た場合は、stream ブロックを http ブロックの中に書いています。unknown directive "stream" と出た場合は、stream モジュールが読み込まれていません(手順1を確認)。
接続の確認は、たとえばDNSなら dig @NginxのIPアドレス example.com で、MySQLなら mysql -h NginxのIPアドレス -u ユーザー名 -p で行います。結果は /var/log/nginx/stream-access.log に記録されます。
注意点
- 1024番未満のポート(25・53など)で待ち受けるには、Nginxのマスタープロセスがroot権限で起動している必要があります。通常のパッケージの起動方法ならそのまま使えます
- 同じポートをほかのソフト(ローカルのDNSサーバーやMySQL本体など)が使っていると、Nginxが起動しません。
ss -lntupで確認してください - ファイアウォールでは、新しく待ち受けるポートだけを、必要な送信元に限って開けてください
- SELinuxが有効な環境では、Nginxが標準外のポートで待ち受けたり、バックエンドに接続したりすることが拒否される場合があります。エラーになったら
/var/log/audit/audit.logを確認してください
まとめ
- stream モジュールの有無は
nginx -Vで確認し、=dynamicならlibnginx-mod-streamかnginx-mod-streamをインストールする streamブロックはhttpブロックの外に書き、設定ファイルはhttpから読み込まれない専用のディレクトリに置く- TCPは
listen 3306;のようにポートだけを書く。tcpというパラメーターはない。UDPはlisten 53 udp; - UDPでは
proxy_responsesと短めのproxy_timeoutでセッションを早く終える allow・denyで接続元を絞り、必要ならバックエンドが対応している場合に限ってproxy_protocol on;で接続元を伝える
関連記事: