rathole

rathole-orgApache-2.0活跃维护 有专家点评

用 Rust 写的高性能轻量反向代理,专门做 NAT 穿透和内网服务暴露,可以当作 frp 和 ngrok 的替代品。

A lightweight and high-performance reverse proxy for NAT traversal, written in Rust. An alternative to frp and ngrok.

v0.5.0发布于 2023-10-01 10:14 UTC14,439star +13 / 7 天852fork同步于 2026-11-10 23:28 UTC

专家点评

上手难度:进阶更新于 2026-11-11

Rust 写的反向隧道,用来顶替 frp 在低配机器上把内网服务暴露到公网

适用场景

  • 在路由器或 ARM 小盒子上做常驻隧道:二进制体积小、无运行时依赖,frp 的 Go 二进制和内存占用在这个场景下更吃紧
  • 需要按服务做权限隔离:每个服务独立 token,一台 client 泄露不会连带其他服务,frp 的共享 token 模型做不到这一点
  • 传输层要能穿过只放行 443 的出口:走 websocket 或 tls 传输伪装成普通 HTTPS 流量,比裸 TCP 更容易过企业防火墙
  • 需要同时转发 UDP:DNS、游戏、QUIC 这类场景 frp 早期版本支持有限,rathole 原生支持 TCP/UDP

踩坑

  • 配置是强耦合的:服务名、token、remote_addr 与 bind_addr 的端口必须两端逐字对齐,任何一处不一致都表现为连接失败而非明确报错,第一次配的时候基本都要对着文档逐字段核一遍
  • 端口范围配置被反复提需求(issue 里 19 个反应),说明当前只能一个服务一条配置;要映射几十个端口时配置文件会膨胀,得自己写脚本生成
  • 数据通道报 early eof 这类连接中断在 issue 里出现过多次,通常和中间网络设备掐断长连接有关,部署在会主动清理空闲连接的链路上时要留意
  • 没有官方的环境变量配置方式(Docker 场景有对应 issue),容器化部署只能挂配置文件,改配置靠热重载而不是重启

同类对比

frp 生态更成熟、文档和示例更多,需要端口范围、插件化或大量现成教程时选它;ngrok 类托管服务适合不想自己维护公网入口的情况。

怎么上手

由 README 提炼,执行前请确认
从 release 页面下载二进制后运行:./rathole server.toml(服务端)与 ./rathole client.toml(客户端);也可使用 Docker 镜像 rapiz1/rathole。
硬件要求按 README 说明

README 未说明具体硬件要求;仅提到二进制可小至约 500KiB,以适配路由器等嵌入式设备,未提及显存、CPU 或 GPU 型号。

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

用 Rust 写的轻量级反向代理,把 NAT 后的服务安全暴露到公网,可替代 frp 和 ngrok

用 Rust 写的高性能轻量反向代理,专门做 NAT 穿透和内网服务暴露,可以当作 frp 和 ngrok 的替代品。

核心能力

  • 用 Rust 实现,吞吐和连接稳定性优于 frp
  • 二进制可小至约 500KiB,适合路由器等嵌入式设备
  • 每个服务强制独立 token 认证,支持 Noise 协议与 TLS 加密
  • 改配置文件热重载即可动态增删服务
  • 支持 TCP 与 UDP 转发,可走 tcp、tls、noise、websocket 传输

容易踩的坑

服务端与客户端的服务名必须完全一致,且 token 必须相同才能通过校验;客户端 remote_addr 的端口必须与服务端 bind_addr 端口一致;若配置文件同时含 [server] 和 [client] 块,需用 --server 或 --client 显式指定运行模式。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v0.5.0
  1. dev-latestDev build (3ee93f8)pre-release2026-08-22 · 2026-08-22 20:04 UTC
    查看更新说明

    Automated build from dev.

    • Commit: 3ee93f8b6d6f7c4e3d4a2c695ecf5e6dd97393a6
    • Rust: 1.92.0
    • Container: ghcr.io/rathole-org/rathole:dev
    • Immutable container: ghcr.io/rathole-org/rathole@sha256:054e8629b84ae3cf3164d9002884469ef9f292978f234aa99d7a28a7d21abbc2
  2. v0.5.0最新正式版2023-10-01 · 2023-10-01 10:14 UTC

    What's Changed

    New transport supported!

    • feat(transport): add websocket transport by @rucciva in https://github.com/rapiz1/rathole/pull/290
    • We dropped linux musl release to save the trouble of linking musl openssl. Now there's only one release for linux x64. Given that it's compiled on ubuntu-latest, the required glibc version should be decent for most users to use.
    • Because of the maintenance burden of linking TLS libraries, we don't compile the TLS feature in the release for embedded platforms anymore. If you need to use it, please build from the source by yourself. We will add it back when a pure rust tls library or a good cross-platform TLS libraries linking solution is available.
    • docs: non root user with systemd by @boenshao in https://github.com/rapiz1/rathole/pull/269
    • chore: fix shield.io badge in README-zh.md by @shiny in https://github.com/rapiz1/rathole/pull/272

    New Contributors

    Full Changelog: https://github.com/rapiz1/rathole/compare/v0.4.8...v0.5.0

  3. v0.4.82023-05-26 · 2023-05-26 09:11 UTC
    查看更新说明

    What's Changed

    This release fixes the build by upgrading dependencies.

    New Contributors

    Full Changelog: https://github.com/rapiz1/rathole/compare/v0.4.7...v0.4.8

  4. v0.4.72022-11-30 · 2022-11-30 04:52 UTC
    查看更新说明

    What's Changed

    Full Changelog: https://github.com/rapiz1/rathole/compare/v0.4.6...v0.4.7

  5. v0.4.62022-11-29 · 2022-11-29 11:23 UTC
    查看更新说明

    What's Changed

    Full Changelog: https://github.com/rapiz1/rathole/compare/v0.4.5...v0.4.6

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    rathole

    rathole-logo

    GitHub stars GitHub release (latest SemVer) GitHub Workflow Status (branch) GitHub all releases Docker Pulls Join the chat at https://gitter.im/rapiz1/rathole

    English | 简体中文

    A secure, stable and high-performance reverse proxy for NAT traversal, written in Rust

    rathole, like frp and ngrok, can help to expose the service on the device behind the NAT to the Internet, via a server with a public IP.

    Features

    • High Performance Much higher throughput can be achieved than frp, and more stable when handling a large volume of connections. See Benchmark
    • Low Resource Consumption Consumes much fewer memory than similar tools. See Benchmark. The binary can be as small as ~500KiB to fit the constraints of devices, like embedded devices as routers.
    • Security Tokens of services are mandatory and service-wise. The server and clients are responsible for their own configs. With the optional Noise Protocol, encryption can be configured at ease. No need to create a self-signed certificate! TLS is also supported.
    • Hot Reload Services can be added or removed dynamically by hot-reloading the configuration file. HTTP API is WIP.

    Quickstart

    A full-powered rathole can be obtained from the release page. Or build from source for other platforms and minimizing the binary. A Docker image is also available.

    The usage of rathole is very similar to frp. If you have experience with the latter, then the configuration is very easy for you. The only difference is that configuration of a service is split into the client side and the server side, and a token is mandatory.

    To use rathole, you need a server with a public IP, and a device behind the NAT, where some services that need to be exposed to the Internet.

    Assuming you have a NAS at home behind the NAT, and want to expose its ssh service to the Internet:

    1. On the server which has a public IP

    Create server.toml with the following content and accommodate it to your needs.

    # server.toml
    [server]
    bind_addr = "0.0.0.0:2333" # `2333` specifies the port that rathole listens for clients
    
    [server.services.my_nas_ssh]
    token = "use_a_secret_that_only_you_know" # Token that is used to authenticate the client for the service. Change to an arbitrary value.
    bind_addr = "0.0.0.0:5202" # `5202` specifies the port that exposes `my_nas_ssh` to the Internet
    

    Then run:

    ./rathole server.toml
    
    1. On the host which is behind the NAT (your NAS)

    Create client.toml with the following content and accommodate it to your needs.

    # client.toml
    [client]
    remote_addr = "myserver.com:2333" # The address of the server. The port must be the same with the port in `server.bind_addr`
    
    [client.services.my_nas_ssh]
    token = "use_a_secret_that_only_you_know" # Must be the same with the server to pass the validation
    local_addr = "127.0.0.1:22" # The address of the service that needs to be forwarded
    

    Then run:

    ./rathole client.toml
    
    1. Now the client will try to connect to the server myserver.com on port 2333, and any traffic to myserver.com:5202 will be forwarded to the client's port 22.

    So you can ssh myserver.com:5202 to ssh to your NAS.

    To run rathole run as a background service on Linux, checkout the systemd examples.

    Configuration

    rathole can automatically determine to run in the server mode or the client mode, according to the content of the configuration file, if only one of [server] and [client] block is present, like the example in Quickstart.

    But the [client] and [server] block can also be put in one file. Then on the server side, run rathole --server config.toml and on the client side, run rathole --client config.toml to explicitly tell rathole the running mode.

    Before heading to the full configuration specification, it's recommend to skim the configuration examples to get a feeling of the configuration format.

    See Transport for more details about encryption and the transport block.

    Here is the full configuration specification:

    [client]
    remote_addr = "example.com:2333" # Necessary. The address of the server
    default_token = "default_token_if_not_specify" # Optional. The default token of services, if they don't define their own ones
    heartbeat_timeout = 40 # Optional. Set to 0 to disable the application-layer heartbeat test. The value must be greater than `server.heartbeat_interval`. Default: 40 seconds
    retry_interval = 1 # Optional. The interval between retry to connect to the server. Default: 1 second
    
    [client.transport] # The whole block is optional. Specify which transport to use
    type = "tcp" # Optional. Possible values: ["tcp", "tls", "noise"]. Default: "tcp"
    
    [client.transport.tcp] # Optional. Also affects `noise` and `tls`
    proxy = "socks5://user:[email protected]:1080" # Optional. The proxy used to connect to the server. `http` and `socks5` is supported.
    nodelay = true # Optional. Determine whether to enable TCP_NODELAY, if applicable, to improve the latency but decrease the bandwidth. Default: true
    keepalive_secs = 20 # Optional. Specify `tcp_keepalive_time` in `tcp(7)`, if applicable. Default: 20 seconds
    keepalive_interval = 8 # Optional. Specify `tcp_keepalive_intvl` in `tcp(7)`, if applicable. Default: 8 seconds
    
    [client.transport.tls] # Necessary if `type` is "tls"
    trusted_root = "ca.pem" # Necessary. The certificate of CA that signed the server's certificate
    hostname = "example.com" # Optional. The hostname that the client uses to validate the certificate. If not set, fallback to `client.remote_addr`
    
    [client.transport.noise] # Noise protocol. See `docs/transport.md` for further explanation
    pattern = "Noise_NK_25519_ChaChaPoly_BLAKE2s" # Optional. Default value as shown
    local_private_key = "key_encoded_in_base64" # Optional
    remote_public_key = "key_encoded_in_base64" # Optional
    
    [client.transport.websocket] # Necessary if `type` is "websocket"
    tls = true # If `true` then it will use settings in `client.transport.tls`
    
    [client.services.service1] # A service that needs forwarding. The name `service1` can change arbitrarily, as long as identical to the name in the server's configuration
    type = "tcp" # Optional. The protocol that needs forwarding. Possible values: ["tcp", "udp"]. Default: "tcp"
    token = "whatever" # Necessary if `client.default_token` not set
    local_addr = "127.0.0.1:1081" # Necessary. The address of the service that needs to be forwarded
    nodelay = true # Optional. Override the `client.transport.nodelay` per service
    retry_interval = 1 # Optional. The interval between retry to connect to the server. Default: inherits the global config
    
    [client.services.service2] # Multiple services can be defined
    local_addr = "127.0.0.1:1082"
    
    [server]
    bind_addr = "0.0.0.0:2333" # Necessary. The address that the server listens for clients. Generally only the port needs to be change.
    default_token = "default_token_if_not_specify" # Optional
    heartbeat_interval = 30 # Optional. The interval between two application-layer heartbeat. Set to 0 to disable sending heartbeat. Default: 30 seconds
    
    [server.transport] # Same as `[client.transport]`
    type = "tcp"
    
    [server.transport.tcp] # Same as the client
    nodelay = true
    keepalive_secs = 20
    keepalive_interval = 8
    
    [server.transport.tls] # Necessary if `type` is "tls"
    pkcs12 = "identify.pfx" # Necessary. pkcs12 file of server's certificate and private key
    pkcs12_password = "password" # Necessary. Password of the pkcs12 file
    
    [server.transport.noise] # Same as `[client.transport.noise]`
    pattern = "Noise_NK_25519_ChaChaPoly_BLAKE2s"
    local_private_key = "key_encoded_in_base64"
    remote_public_key = "key_encoded_in_base64"
    
    [server.transport.websocket] # Necessary if `type` is "websocket"
    tls = true # If `true` then it will use settings in `server.transport.tls`
    
    [server.services.service1] # The service name must be identical to the client side
    type = "tcp" # Optional. Same as the client `[client.services.X.type]
    token = "whatever" # Necessary if `server.default_token` not set
    bind_addr = "0.0.0.0:8081" # Necessary. The address of the service is exposed at. Generally only the port needs to be change.
    nodelay = true # Optional. Same as the client
    
    [server.services.service2]
    bind_addr = "0.0.0.1:8082"
    

    Logging

    rathole, like many other Rust programs, use environment variables to control the logging level. info, warn, error, debug, trace are available.

    RUST_LOG=error ./rathole config.toml
    

    will run rathole with only error level logging.

    If RUST_LOG is not present, the default logging level is info.

    Tuning

    From v0.4.7, rathole enables TCP_NODELAY by default, which should benefit the latency and interactive applications like rdp, Minecraft servers. However, it slightly decreases the bandwidth.

    If the bandwidth is more important, TCP_NODELAY can be opted out with nodelay = false.

    Benchmark

    rathole has similar latency to frp, but can handle a more connections, provide larger bandwidth, with less memory usage.

    For more details, see the separate page Benchmark.

    However, don't take it from here that rathole can magically make your forwarded service faster several times than before. The benchmark is done on local loopback, indicating the performance when the task is cpu-bounded. One can gain quite a improvement if the network is not the bottleneck. Unfortunately, that's not true for many users. In that case, the main benefit is lower resource consumption, while the bandwidth and the latency may not improved significantly.

    http_throughput tcp_bitrate udp_bitrate mem

    Planning

    • HTTP APIs for configuration

    Out of Scope lists features that are not planned to be implemented and why.