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; で接続元を伝える

関連記事:

お気軽にご相談ください

お見積りへ お問い合わせへ
▲