用 SSH 反向端口转发,将本机代理给远程服务器用
为什么学这个
最近我在远程服务器上做开发和装环境,遇到一个很实际的问题:服务器在使用 pip 安装 Python 包时,由于网络波动导致连接超时或下载中断,出现 ReadTimeoutError 或 ConnectionError 等异常,或者有些资源无法直接访问,但我本机已配置好代理,能够正常安装python依赖包。
我的目标很明确:
让远程服务器复用我本机的代理能力。
一开始我以为只要在服务器上“配置个代理”就行,后来才发现事情没那么简单。核心原因是:
- 我本机的代理端口,比如
127.0.0.1:<本机代理端口>,默认只对本机自己可见 - 远程服务器访问不到我电脑的
127.0.0.1 - 所以必须借助一条SSH 隧道,把我本机的代理端口“带到”远程服务器上去
这次我学到的核心技术,就是:
SSH 反向端口转发(Remote Port Forwarding)
核心内容 / 步骤
1. 场景和目标
我的环境是:
- 本机:Windows
- 远程服务器:Windows
- 本机代理端口:
<本机代理端口> - 远程服务器地址:
<远程服务器地址> - 远程登录用户:
admin
我要实现的效果是:
在远程服务器上访问一个本地端口,比如
127.0.0.1:17897,实际流量通过 SSH 隧道回到我本机,再交给代理工具的127.0.0.1:<本机代理端口>处理。
2. 先确认本机代理端口可用
第一步不是急着连服务器,而是先确认我本机的 代理工具 端口本身是通的。
在本机 PowerShell 里测试:
curl.exe -x http://127.0.0.1:<本机代理端口> https://www.google.com
如果能返回网页内容,说明本机 代理工具 端口没问题。
这一步很重要。
因为如果本机代理本来就不通,后面的 SSH 转发再怎么配也没意义。
3. 用 SSH 建立反向端口转发
真正关键的命令是:
ssh -N -R 17897:127.0.0.1:<本机代理端口> admin@<远程服务器地址>
这条命令的含义可以拆成三部分理解:
ssh:连接远程服务器-N:只建立隧道,不执行远程命令-R 17897:127.0.0.1:<本机代理端口>:在远程服务器上开一个17897端口,把它转发到我本机的127.0.0.1:<本机代理端口>
也就是说,执行成功后:
- 远程服务器上的代理入口变成了:
127.0.0.1:17897 - 实际出口仍然是我本机 代理工具:
127.0.0.1:<本机代理端口>
执行这条命令后,窗口会一直挂着不动,这是正常现象。
这个窗口不能关,因为它本身就是隧道。
4. 在远程服务器上设置代理
隧道打通后,还需要告诉远程服务器上的程序:以后访问网络走这个代理。
因为我的远程服务器是 Windows,而且终端有时是 CMD,有时是 PowerShell,所以这里要区分两种写法。
如果远程终端是 CMD
set http_proxy=http://127.0.0.1:17897
set https_proxy=http://127.0.0.1:17897
如果远程终端是 PowerShell
$env:http_proxy="http://127.0.0.1:17897"
$env:https_proxy="http://127.0.0.1:17897"
这一点是我这次踩坑最多的地方之一。
同样是 Windows,CMD 和 PowerShell 的环境变量写法并不一样。
5. 测试代理是否真的生效
在远程终端里测试:
curl.exe -I https://www.google.com
如果看到类似输出:
HTTP/1.1 200 Connection established
HTTP/1.1 200 OK
基本就能判断代理已经生效了。
尤其是这行:
HTTP/1.1 200 Connection established
它说明请求是先通过代理建立了 HTTPS 隧道,再去访问目标网站。
这比单纯返回网页内容更能证明“确实走了代理”。
6. 在 VS Code Remote SSH 里自动带上这条隧道
手工执行 ssh -N -R ... 能跑通之后,我又继续做了一步优化:
让 VS Code 通过 SSH 连接服务器时,自动带上这个反向转发。
做法是在本机的 SSH 配置文件里加入:
Host myserver
HostName <远程服务器地址>
User admin
RemoteForward 17897 127.0.0.1:<本机代理端口>
配置文件一般在:
C:\Users\你的用户名.ssh\config
然后在 VS Code 里连接 myserver,这样 VS Code 发起 SSH 连接时,会自动建立:
远程 127.0.0.1:17897 -> 本机 127.0.0.1:<本机代理端口>
之后我只需要在 VS Code 的远程终端里设置:
$env:http_proxy="http://127.0.0.1:17897"
$env:https_proxy="http://127.0.0.1:17897"
就能直接在远程开发环境里用代理了。
7. 让 VS Code 终端自动带代理变量
为了避免每次都手动输入环境变量,我还可以在 VS Code 远程窗口里配置 terminal 环境变量,或者修改远程 PowerShell 的 $PROFILE。
例如在 VS Code Remote Settings JSON 里加:
{
"terminal.integrated.env.windows": {
"http_proxy": "http://127.0.0.1:17897",
"https_proxy": "http://127.0.0.1:17897"
}
}
这样每次新开远程终端,代理变量都会自动带上。
遇到的问题与解决方法
问题 1:CMD 和 PowerShell 语法不同
在远程服务器上,我有时进入的是 CMD,有时进入的是 PowerShell。
-
CMD 写法:
set http_proxy=http://127.0.0.1:17897 -
PowerShell 写法:
$env:http_proxy="http://127.0.0.1:17897"
如果语法用错,通常不会得到想要的效果。
问题 2:PowerShell 里的 curl 不是我以为的那个 curl
在 PowerShell 里执行:
curl -I https://www.google.com
结果触发的是 Invoke-WebRequest,还提示输入 Uri。
解决方法有两个:
显式调用真正的 curl
curl.exe -I https://www.google.com
或者直接用 PowerShell 命令
(Invoke-WebRequest -Uri https://www.google.com).StatusCode
这个坑很典型,也很容易误判成“代理没通”。
问题 3:VS Code Remote SSH 连上了,不代表代理隧道也有了
这是一个理解上的关键点。
- VS Code Remote SSH 负责的是“我能不能远程开发”
- SSH
-R/RemoteForward负责的是“远程服务器能不能借用我本机代理”
这两者不是一回事。
如果只连了 VS Code,但没带 RemoteForward,远程端不会自动出现 127.0.0.1:17897 这个入口。
解决方法有两个:
- 保留一条单独的
ssh -N -R ...隧道连接 - 在
~/.ssh/config里为该主机加RemoteForward
我最后采用的是第二种,体验更好。
问题 4:代理工具 的“流量隧道管理”看起来很像,但不是一个东西
我中间还研究了 代理工具 自带的“流量隧道管理”功能,以为它和 SSH 反向转发作用相同。
后来搞清楚了:
- 代理工具 流量隧道:主要是在本机开一个端口做转发
- SSH
-R:是在远程服务器上开一个端口,把流量带回本机
所以它们不是替代关系。
对“让远程服务器借用我本机代理”这个需求来说,核心还是 SSH RemoteForward。
收获与总结
这次实践最大的收获,不是单纯“把代理配通了”,而是把几个容易混在一起的概念真正理顺了:
1. 本机代理端口默认只对本机可见
像 代理工具 的 127.0.0.1:<本机代理端口>,远程服务器默认访问不到。
这也是为什么必须借助 SSH 隧道。
2. SSH 反向端口转发非常适合“把本机能力借给远程机器”
我现在对这条命令的理解已经很清楚了:
ssh -N -R 17897:127.0.0.1:<本机代理端口> admin@<远程服务器地址>
本质就是:
在远程开入口,在本机做出口。
3. VS Code Remote SSH 和代理隧道是两层东西
VS Code 负责远程开发连接,RemoteForward 负责远程代理能力。
理解这一点之后,很多“为什么我明明连上服务器了,代理却没用”的问题就很容易解释了。
4. 最终可用方案
我最终沉淀下来的实用方案是:
-
本机 代理工具 正常运行,端口如
<本机代理端口> -
SSH config 中为远程主机配置:
Host myserver HostName <远程服务器地址> User admin RemoteForward 17897 127.0.0.1:<本机代理端口> -
VS Code 通过
myserver连接 -
远程终端中配置(Windows系统):
$env:http_proxy="http://127.0.0.1:17897" $env:https_proxy="http://127.0.0.1:17897" -
远程终端中配置(Linux系统):
export http_proxy="http://127.0.0.1:17897" export https_proxy="http://127.0.0.1:17897"