SSHポートフォワード(トンネル)は、SSHの暗号化された接続の中に「別の通信を通す」機能です。もっともよくある用途が、外部からは直接つなげないリモートのデータベースに、手元のツールから接続することです。多くのレンタルサーバーやクラウドでは、セキュリティのためMySQLなどのデータベースを外部公開していません。しかしSSHでログインできるなら、SSH接続をトンネルにして、そのデータベースへ安全にアクセスできます。
この記事では、実際にレンタルサーバー(エックスサーバー)のリモートMySQLへ、SSHトンネル経由で手元から接続する例を動かしながら、ポートフォワードの使い方を整理します。-L(ローカルフォワード)を中心に、つまずきやすい「転送先はどこから見た宛先か」という点や、-N・-fの使い方まで解説します。
- 基本は
ssh -L 手元のポート:宛先ホスト:宛先ポート ユーザー@サーバーです。 - 手元の
localhost:ポートへつなぐと、サーバー経由で宛先に届きます。 - 宛先ホストは「サーバー側から見た宛先」として解決されます(ここが最重要)。
- 外部非公開のリモートDBに、手元のDBツールから接続できるのが定番用途です。
- シェルを使わず接続だけ張るなら
-N、背後で動かすなら-fを付けます。 - SOCKSプロキシは
-D、逆方向(リモート→手元)は-Rです。
接続設定の集約は~/.ssh/config、鍵認証の準備はssh-keygen、ポート変更済みサーバーの扱いはSSHポート番号の変更もあわせて参考になります。
ローカルフォワード(-L)の基本
もっとも使うのが-L(ローカルフォワード)です。書式は-L 手元のポート:宛先ホスト:宛先ポート。「手元の指定ポートへの通信を、SSHサーバーを経由して宛先ホスト:宛先ポートへ届ける」という意味です。
# 手元の 13306 → サーバー経由 → リモートDB(3306) へトンネルを張る ssh -L 13306:db-host.example.com:3306 user@server.example.com # 13306 … 手元PCで開くポート(好きな番号でよい) # db-host.example.com … 「サーバーから見た」DBのホスト名 # 3306 … DBのポート(MySQLの既定) # 別のターミナルで、手元の 13306 に接続すると # サーバー経由でリモートDBにつながる mysql -h 127.0.0.1 -P 13306 -u dbuser -p
実際にレンタルサーバーで試しました。ssh -L 13306:(DBホスト):3306 (ユーザー)@(サーバー)でトンネルを張り、別のターミナルから手元の127.0.0.1:13306に対してMySQLクライアントで接続したところ、リモートのMySQLに正しくつながり、SELECT VERSION()で5.7.27、データベース名まで取得できました。ポイントは、このDBホストが外部には公開されておらず、手元から直接は接続できないことです。それでも、SSHでログインできる権限さえあれば、SSH接続をトンネルにしてDBへ安全に到達できます。ローカル開発環境のツール(phpMyAdminやDBクライアント)から本番DBを覗く、といった作業がこれで実現できます。通信はSSHで暗号化されるため、安全性の面でも安心です。
【最重要】転送先は「サーバー側から見た宛先」
ポートフォワードで最も誤解しやすいのが、-L 手元:宛先ホスト:宛先ポートの「宛先ホスト」は、どこから見た名前かという点です。答えは「SSHでログインするサーバーから見た宛先」です。手元のPCから見た名前ではありません。
# パターン1: DBがSSHサーバー自身にある場合 → localhost ssh -L 13306:localhost:3306 user@server.example.com # 「サーバーにとっての localhost:3306」= サーバー上のMySQL # パターン2: DBが別のホストにある場合 → そのホスト名/IP ssh -L 13306:db-internal.example.com:3306 user@server.example.com # db-internal は「サーバーから見て」到達できる名前である必要がある # 手元からは db-internal に直接つながらなくてよい # (サーバーが中継してくれるため)
実サーバーでの検証でも、トンネルの宛先に指定したDBホストは「サーバー側のネットワークから到達できる名前」でした。手元のPCからそのDBホストに直接つなごうとしても、外部非公開なので届きません。しかしトンネルでは宛先への接続をサーバーが代わりに行うため、手元から届かない相手にもアクセスできるのです。よくある間違いが、-L 13306:localhost:3306のlocalhostを「手元のPC」と勘違いすることです。ここでのlocalhostは「SSHでログインするサーバー自身」を指します。「宛先ホストは、SSHサーバーがそこへ接続しに行く先」と理解すれば、間違えません。DBがSSHサーバー自身で動いているならlocalhost、別のDBサーバーならサーバーから見たそのホスト名を書きます。
-N・-f(接続だけを背後で張る)
トンネルを張るとき、サーバー上でシェル操作をする必要はありません。-N(コマンドを実行しない)と-f(バックグラウンドに回る)を組み合わせると、トンネル専用の接続をすっきり張れます。
# -N: リモートでコマンドを実行しない(トンネルだけ) # -f: 認証後にバックグラウンドへ回る ssh -f -N -L 13306:db-host:3306 user@server.example.com # これでプロンプトが戻り、裏でトンネルが張られたまま # 手元の 13306 を使う作業ができる # ポートが22以外なら -p も(configに書けば省ける) ssh -f -N -p 10022 -L 13306:db-host:3306 user@server.example.com
実サーバーでのトンネルも、-f -N -L ...の形でバックグラウンドに接続だけを張り、別ターミナルからDB接続するという流れで動作しました。-Nを付けないと通常のログインシェルも開いてしまい、-fを付けないとそのターミナルがトンネルに占有されます。「トンネルを張りっぱなしにして作業したい」ときは-f -Nがセットです。毎回オプションを書くのが面倒なら、~/.ssh/configにLocalForward 13306 db-host:3306と書いておく方法もあります。
-D(SOCKSプロキシ)と-R(リモートフォワード)
-L以外にも、方向や用途の違う転送があります。-DはSOCKSプロキシ(ブラウザ等の通信をまるごとサーバー経由にする)、-Rはリモートフォワード(-Lと逆に、サーバー側のポートを手元へ転送)です。
# -D: 動的フォワード(SOCKSプロキシ) # 手元の 1080 を SOCKS プロキシにして、通信をサーバー経由にする ssh -D 1080 user@server.example.com # ブラウザのプロキシ設定を SOCKS5 127.0.0.1:1080 にすると # すべての通信がサーバーを経由する(社内網アクセス等) # -R: リモートフォワード(-L と逆方向) # サーバーの 8080 への接続を、手元の 3000 へ転送する ssh -R 8080:localhost:3000 user@server.example.com # 手元で動かしている開発サーバー(3000)を、 # リモート側から一時的に見せたいときに使う
-Dは、指定した手元のポートをSOCKSプロキシとして動かし、ブラウザなどの通信をまとめてサーバー経由にできます(社内ネットワークへのアクセスや、接続元をサーバーに見せたいときに便利です)。-Rは-Lと向きが逆で、サーバー側のポートへの接続を手元へ転送します。「手元で動かしている開発中のサーバーを、リモート側から一時的にアクセスできるようにする」といった使い方があります。日常でよく使うのは圧倒的に-Lなので、まずは-Lを確実に押さえ、-D・-Rは「そういう方向もある」と覚えておけば十分です。
トンネルの閉じ方
-f -Nでバックグラウンドに張ったトンネルは、作業が終わったらそのSSHプロセスを終了して閉じます。開いているポートから、対応するプロセスを見つけて終了します。
# Linux/Mac: ポートを使っているプロセスを探して終了 lsof -i :13306 # トンネルの ssh プロセスを確認 kill <表示されたPID> # もっと手軽に(ポートを直接指定して終了) # pkill -f "13306:" # フォワード指定で絞って終了 # Windows(PowerShell): ポートからPIDを調べて終了 # netstat -ano | findstr 13306 # taskkill /PID <PID> /F # -f を付けず前面で張っている場合は Ctrl+C で終了
バックグラウンドのトンネルは、明示的に終了しないと張られたまま残るため、使い終わったら閉じる習慣をつけましょう。ps・killの記事で解説したように、lsof -i :ポート(またはss/netstat)でトンネルのSSHプロセスを特定し、killで終了します。前面で張っている(-fなし)場合は、そのターミナルでCtrl+Cを押せば閉じられます。
主なオプション一覧
ポートフォワードで使うオプションをまとめます。
| オプション | 働き |
|---|---|
-L 手元:宛先:ポート |
ローカルフォワード(手元→サーバー経由→宛先) |
-R サーバー:宛先:ポート |
リモートフォワード(-Lと逆方向) |
-D ポート |
動的フォワード(SOCKSプロキシ) |
-N |
リモートでコマンドを実行しない(トンネル専用) |
-f |
認証後にバックグラウンドへ回る |
-p 番号 |
SSHのポート(22以外のとき) |
よくある失敗
宛先のlocalhostを「手元」と勘違いする
-L 13306:localhost:3306のlocalhostはSSHサーバー自身です。手元PCではありません。
手元のポートが既に使われている
13306など空いている番号を選びます。使用中だと「Address already in use」になります。
接続先を127.0.0.1にし忘れる
ツール側はサーバー名ではなく手元の127.0.0.1:手元ポートに接続します。
トンネルを閉じ忘れる
-fで背後に張ったトンネルは残り続けます。lsof/netstatで見つけて終了します。
-Nを付けず余計なシェルが開く
トンネル専用なら-Nを付けます。-fと合わせて-f -Nが定番です。
よくある質問
ssh -L 13306:DBホスト:3306 user@serverでトンネルを張り、手元の127.0.0.1:13306に接続すると、SSHサーバー経由でDBにつながります。実サーバーでも、外部非公開のMySQLにこの方法で接続できることを確認しています。通信はSSHで暗号化されるので安全です。localhost、別のDBサーバーにあるなら「サーバーから到達できる」そのホスト名を書きます。-Nはリモートでコマンドを実行せず、トンネルだけを張るためです。-fは認証後にバックグラウンドへ回り、プロンプトを返します。トンネルを張りっぱなしで別の作業をしたいときは、ssh -f -N -L ...のように両方を付けるのが定番です。-L(ローカルフォワード)は「手元→サーバー経由→宛先」で、リモートのサービスに手元からアクセスします。-R(リモートフォワード)はその逆で、「サーバー側のポートへの接続を手元へ転送」します。手元で動かしている開発サーバーをリモートから見せたいときなどに使います。日常でよく使うのは-Lです。-fでバックグラウンドに張った場合は、そのSSHプロセスを終了します。lsof -i :ポート番号(またはWindowsはnetstat -ano | findstr ポート)でプロセスを特定し、kill/taskkillで終了してください。-fを付けず前面で張っている場合は、そのターミナルでCtrl+Cを押せば閉じられます。まとめ
- 基本は
ssh -L 手元ポート:宛先ホスト:宛先ポート user@server。 - 手元の
127.0.0.1:手元ポートにつなぐと、サーバー経由で宛先に届きます。 - 宛先ホストは「サーバー側から見た宛先」。
localhostはサーバー自身です。 - トンネル専用は
-f -N、SOCKSは-D、逆方向は-R。 - 使い終わったトンネルは
lsof/netstatで見つけて閉じます。
SSHポートフォワードは、「外部非公開のサービスに、SSHの権限だけで安全に到達する」ための強力な仕組みです。まずは-LでリモートDBに接続する使い方を押さえれば、本番データベースの確認や管理がぐっと楽になります。~/.ssh/configにLocalForwardを書いておけば、毎回の長いコマンドも不要になります。

