Skip to content

隧道管理

本章覆盖 HyperFRP Panel 后端的隧道(proxy / frpc 隧道)管理接口,共 16 个:本人隧道列表、分页搜索、创建、批量删除、批量启停、随机远程端口、详情、更新、删除、启停开关、绑定域名、配置预览、快捷启动命令、单隧道强制下线,以及单隧道 / 合并两种格式的配置文件下载。

鉴权基调:本章全部 16 个接口都需要登录,通过 Authorization: Bearer <token> 请求头携带 JWT(仅支持请求头方式,不接受 URL 参数传递),Bearer 令牌的获取与使用方式详见认证指南。所有路由经过全站 WAF 防护,且逐一挂载 NoCache 缓存中间件(响应携带 Cache-Control: no-store, no-cache, must-revalidate, private,禁止任何缓存)。除两个配置下载端点直接返回二进制附件外,其余接口均返回统一包络 {"code": 0, "message": "...", "data": ...},错误码与 HTTP 状态码对齐;与具体接口无关的通用约定见通用约定

以下各接口的「错误场景」表只列接口特有分支。所有接口共有的鉴权失败分支不再逐条重复:

场景HTTP说明
未携带 Authorization 请求头401{"code":401,"message":"未提供认证令牌"}
JWT 无效或已过期401{"code":401,"message":"无效的令牌或令牌已过期"}
服务端会话失效401{"code":401,"message":"会话无效或已过期,请重新登录"}
账户已被封禁403{"code":403,"message":"您的账户已被封禁","data":{"isBanned":true,"ban_reason":"..."}}
认证版本或账户状态变更401{"code":401,"message":"账户状态或认证版本已变更,请重新登录"}

隧道对象模型

本章接口返回隧道对象时有两种形态,字段以源码 ProxyResponseDTOmodels.Proxy 的 JSON 标签为准:

  • 精简 DTO(下称「隧道 DTO」):仅 GET /api/proxies 列表接口使用,隐藏 GORM 模型细节,node 为精简节点对象;
  • 完整模型(下称「完整隧道」):创建、详情、更新、启停、绑定域名、搜索等接口返回,字段更多,node 为预载的节点对象。

隧道 DTO 字段(GET /api/proxies 列表专用)

字段类型说明
idnumber隧道 ID
namestring隧道名称
typestring隧道类型,枚举见创建接口
nodeIdnumber所属节点 ID
localIpstring本地服务 IP
localPortnumber本地服务端口
remotePortnumber远程端口(tcp/udp/stcp/sudp 使用);http/https/tcpmux 恒为 0
domainstring绑定域名,多个以英文逗号分隔(http/https/tcpmux 使用);其他类型为空串
commentstring备注
statusstring隧道状态,枚举:pending / stopped / running / error / disabled
enabledUserboolean用户侧启用开关
statusAdminboolean管理员侧启用开关(为 false 时隧道无法运行)
adminReasonstring管理员处置原因(如禁用说明)
trafficInBytesnumber累计入站流量(字节)
trafficOutBytesnumber累计出站流量(字节)
useEncryptionboolean是否启用传输加密
useCompressionboolean是否启用传输压缩
transportProtocolstring传输协议:tcp(默认)/ quic(节点开启 QUIC 时可用)
proxyProtocolVersionstringProxy Protocol 版本:空串(不启用)/ v1 / v2;仅 tcp/udp 支持
ccProtectionEnabledbooleanCC 防护开关
bandwidthLimitstring带宽上限(如 10MB),未设置为空串
maxConnectionsnumber最大连接数,0 表示不限制
secretConfiguredboolean敏感配置(sk 等访问密钥)是否已设置;只回布尔,永不返回明文或密文
configurationCompleteboolean权威协议配置在最近一次写入时是否完整可消费
protocolConfigobject | 不出现解析后的非敏感权威协议配置,结构见创建接口 protocolConfig 说明;未配置时省略
createdAtstring创建时间(RFC3339)
updatedAtstring更新时间(RFC3339)
nodeobject | 不出现精简节点对象,结构见下;节点缺失时省略

精简节点对象 nodeProxyNodeDTO)字段:

字段类型说明
idnumber节点 ID
namestring节点名称
hoststring节点主机地址(域名或 IP)
portnumberFRPS 主连接端口
domainstring节点域名
locationstring地理位置描述(如 中国 广东 广州
statusstring节点状态:online / api_only / unreachable / frps_stopped / offline / maintenance

完整隧道字段(创建 / 详情 / 更新 / 启停 / 绑定域名 / 搜索返回)

在隧道 DTO 字段之外,完整模型还有以下字段;两者重复的字段语义相同,不再重复列出。

字段类型说明
userIdnumber所属用户 ID(即当前登录用户)
nodeobject | 不出现嵌套节点对象。创建 / 详情 / 更新响应会预载完整节点模型;启停与绑定域名响应不预载(该键省略);搜索响应预载完整节点模型(含节点运行指标等全部非凭据字段,节点 token 等长期凭据字段从不外泄)
subDomainstring恒为空串(历史兼容虚拟字段)
locationsstring恒为空串(路径路由经由 protocolConfig.locations 表达)
hostHeaderRewritestring恒为空串(经由 protocolConfig.hostHeaderRewrite 表达)
headersstring恒为空串(经由 protocolConfig.headers 表达)
pluginstring恒为空串(HTTPS 证书插件由配置生成器按 pluginCrtPath / pluginKeyPath 固定输出 https2http
pluginParamsstring恒为空串
pluginCrtPathstringHTTPS 插件证书路径(仅 https 类型使用)
pluginKeyPathstringHTTPS 插件私钥路径(仅 https 类型使用)
extraParamsstring恒为空串(保留字段)
lastActiveAtnull恒为 null(虚拟字段,未启用)
forceOfflineGenerationnumber持久强制下线代次:每次强制下线 / 强制注销 / 访问密钥重置时原子递增,FRPS 发现代次变化即关闭运行实例
cc_rulesobject | 不出现CC 防护规则 { "rate": number, "burst": number }(rate 为每秒令牌数,burst 为突发容量);未开启 CC 防护时省略。注意这是对象中唯一使用下划线命名的键
protocolConfigobject | 不出现同隧道 DTO;未配置或版本不受支持时省略
user恒不出现(omitempty 且不预载)

列表与查询

GET /api/proxies

返回当前登录用户的全部隧道(不分页),按创建时间倒序排列。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求示例

bash
curl https://api.hyperfrp.com/api/proxies \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies', {
  headers: {
    'Authorization': 'Bearer <token>',
  },
});
const data = await res.json();
console.log(data);
python
import requests

res = requests.get(
    'https://api.hyperfrp.com/api/proxies',
    headers={'Authorization': 'Bearer <token>'},
)
data = res.json()
print(data)
go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.hyperfrp.com/api/proxies", nil)
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

data 为隧道 DTO 数组(可为空数组),字段见「隧道 DTO 字段」总表。

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": [
    {
      "id": 42,
      "name": "web-ssh",
      "type": "tcp",
      "nodeId": 2,
      "localIp": "127.0.0.1",
      "localPort": 22,
      "remotePort": 30001,
      "domain": "",
      "comment": "内网 SSH",
      "status": "running",
      "enabledUser": true,
      "statusAdmin": true,
      "adminReason": "",
      "trafficInBytes": 1048576,
      "trafficOutBytes": 2097152,
      "useEncryption": true,
      "useCompression": false,
      "transportProtocol": "tcp",
      "proxyProtocolVersion": "",
      "ccProtectionEnabled": false,
      "bandwidthLimit": "",
      "maxConnections": 0,
      "secretConfigured": false,
      "configurationComplete": true,
      "createdAt": "2026-09-19T10:00:00+08:00",
      "updatedAt": "2026-09-19T12:30:00+08:00",
      "node": {
        "id": 2,
        "name": "华南-广东-01",
        "host": "gd01.hyperfrp.com",
        "port": 7000,
        "domain": "gd01.hyperfrp.com",
        "location": "中国 广东 广州",
        "status": "online"
      }
    }
  ]
}

错误场景

场景HTTP说明
数据库查询失败500{"code":500,"message":"获取隧道列表失败"}

分页搜索当前用户的隧道,支持关键词、类型、节点、启用状态与运行状态过滤,以及白名单字段排序。响应返回完整隧道模型(非 DTO)。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求参数(Query)

参数类型必填说明
keywordstring按隧道名称模糊匹配(SQL LIKE %keyword%
typestring按类型精确过滤(tcp / udp / http / https / stcp / sudp / xtcp / tcpmux
nodeIdnumber按节点 ID 精确过滤(大于 0 时生效)
enabledstring按用户侧启用状态过滤:仅字符串 "true" 视为 true,其余任何值(含 "false")均按 false 过滤;留空不过滤
statusstring按运行状态精确过滤(pending / stopped / running / error / disabled
pagenumber页码,默认 1,上限 10000
pageSizenumber每页条数,默认 10,上限 100
sortBystring排序字段,白名单:createdAt / updatedAt / name / type / status / enabled / localPort / remotePort;其他值一律回退 createdAt
sortOrderstringASC(大小写均可)升序,其余一律降序(默认 DESC

请求示例

bash
curl "https://api.hyperfrp.com/api/proxies/search?keyword=web&type=tcp&nodeId=2&enabled=true&page=1&pageSize=10&sortBy=updatedAt&sortOrder=DESC" \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/search?keyword=web&type=tcp&nodeId=2&enabled=true&page=1&pageSize=10&sortBy=updatedAt&sortOrder=DESC', {
  headers: {
    'Authorization': 'Bearer <token>',
  },
});
const data = await res.json();
console.log(data);
python
import requests

res = requests.get(
    'https://api.hyperfrp.com/api/proxies/search?keyword=web&type=tcp&nodeId=2&enabled=true&page=1&pageSize=10&sortBy=updatedAt&sortOrder=DESC',
    headers={'Authorization': 'Bearer <token>'},
)
data = res.json()
print(data)
go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.hyperfrp.com/api/proxies/search?keyword=web&type=tcp&nodeId=2&enabled=true&page=1&pageSize=10&sortBy=updatedAt&sortOrder=DESC", nil)
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

字段类型说明
proxiesarray完整隧道模型数组,字段见「完整隧道字段」总表;node 为预载的完整节点对象。注意:搜索结果不会回填 secretConfiguredprotocolConfig 虚拟字段——secretConfigured 恒为 falseprotocolConfig 恒省略,需以详情接口为准
pagination.totalnumber过滤后的总条数
pagination.pagenumber当前页码(归一化后的实际值)
pagination.pageSizenumber每页条数(归一化后的实际值)
pagination.totalPagesnumber总页数(向上取整)

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": {
    "proxies": [
      {
        "id": 42,
        "userId": 1001,
        "nodeId": 2,
        "node": { "id": 2, "name": "华南-广东-01", "host": "gd01.hyperfrp.com" },
        "name": "web-ssh",
        "type": "tcp",
        "localIp": "127.0.0.1",
        "localPort": 22,
        "remotePort": 30001,
        "domain": "",
        "subDomain": "",
        "locations": "",
        "hostHeaderRewrite": "",
        "headers": "",
        "plugin": "",
        "pluginParams": "",
        "pluginCrtPath": "",
        "pluginKeyPath": "",
        "useEncryption": true,
        "useCompression": false,
        "transportProtocol": "tcp",
        "proxyProtocolVersion": "",
        "extraParams": "",
        "status": "running",
        "enabledUser": true,
        "statusAdmin": true,
        "adminReason": "",
        "trafficInBytes": 1048576,
        "trafficOutBytes": 2097152,
        "lastActiveAt": null,
        "comment": "内网 SSH",
        "createdAt": "2026-09-19T10:00:00+08:00",
        "updatedAt": "2026-09-19T12:30:00+08:00",
        "ccProtectionEnabled": false,
        "forceOfflineGeneration": 0,
        "configurationComplete": true,
        "secretConfigured": false
      }
    ],
    "pagination": {
      "total": 1,
      "page": 1,
      "pageSize": 10,
      "totalPages": 1
    }
  }
}

错误场景

场景HTTP说明
查询参数绑定失败400{"code":400,"message":"无效的查询参数"}
统计总数失败500{"code":500,"message":"统计隧道数量失败"}
查询列表失败500{"code":500,"message":"搜索隧道失败"}

创建与批量

POST /api/proxies

创建一条属于当前用户的隧道。服务端在事务内对用户与节点行加锁后完成全部校验并写入,避免并发创建下的端口 / 域名 / 配额竞争。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

隧道类型枚举(type)

说明
tcpTCP 转发,需 remote_port
udpUDP 转发,需 remote_port
httpHTTP 反向代理,需 domains(按域名路由)
httpsHTTPS 反向代理,需 domains,可选本地证书插件路径
stcp安全 TCP(secret key 访问),需 remote_portsecretConfig.sk
sudp安全 UDP(secret key 访问),需 remote_portsecretConfig.sk
xtcpP2P 穿透 TCP,需 secretConfig.sk
tcpmuxTCP 复用(httpconnect 多路复用器),需 domainsprotocolConfig.multiplexer

类型值大小写不敏感(服务端统一转小写),其余值一律拒绝。

请求体

字段类型必填说明
namestring隧道名称,1–100 个字符,仅允许字母、数字、下划线与短横线;同一用户内唯一
typestring隧道类型,枚举见上表
local_ipstring本地服务 IP(如 127.0.0.1),≤255 字符
local_portnumber本地服务端口,1–65535
remote_portnumber类型相关远程端口。tcp/udp/stcp/sudp 必填:1–65535、在节点允许的端口范围内且未被该节点其他隧道占用;http/https/tcpmux 忽略(强制置 0)
domainsstring类型相关绑定域名。注意是英文逗号分隔的字符串而非数组(如 "a.example.com,b.example.com")。http/https 必填、tcpmux 必填;tcp/udp/stcp/sudp 强制清空。每段域名 trim + 转小写、去重,单段 ≤253 字符,拼接后总长 ≤255 字符;仅允许字母 / 数字 / 连字符 / 点(不支持 * 通配符),拒绝 javascript: / data: / vbscript: / file: 前缀
commentstring备注,≤500 字符
nodeIdnumber目标节点 ID(注意此键为小驼峰)
use_encryptionboolean是否启用传输加密,默认 false
use_compressionboolean是否启用传输压缩,默认 false
transport_protocolstring传输协议:空串视为 tcp;仅允许 tcp / quicquic 要求目标节点已开启 QUIC
proxy_protocol_versionstringProxy Protocol 版本:空串或 disabled 视为不启用;tcp 仅允许 v1 / v2,udp 仅允许 v2,其他类型提交任何非空值均报错
plugin_crt_pathstringHTTPS 插件证书路径,仅 https 类型保留(其他类型强制清空),≤255 字符
plugin_key_pathstringHTTPS 插件私钥路径,约束同上
cc_protection_enabledbooleanCC 防护开关,默认 false。开启后未显式提交规则时套用默认规则 {"rate":20,"burst":40}
protocolConfigobject非敏感权威协议配置扩展字段(可选),结构见下
secretConfigobject类型相关敏感参数配置,结构见下。stcp/sudp/xtcp 必填sk 非空(fail-closed);普通类型可选。提交后 AES-GCM 加密落库,任何接口不再返回明文 / 密文

protocolConfigProxyAuthorizedConfig)扩展字段:表字段(type / remote_port / domains / use_encryption 等)是权威来源,此对象只补充表内不存在的扩展项,全部可选、经规范化后落库——

字段类型说明
customDomainsstring[]附加路由域名(会 trim / 小写 / 排序 / 去重)
subdomainstring子域名
locationsstring[]http/https 路由路径
hostHeaderRewritestringHost 头重写
headers / responseHeadersobject附加请求 / 响应头(键做 canonical MIME 规范化,各 ≤20 条,键值不得含控制字符)
routeByHTTPUserstring按用户路由
groupstring负载均衡组名(组密钥走 secretConfig.groupKey
multiplexerstringtcpmux 复用器,仅允许 httpconnect;tcpmux 类型必填
transportProtocolstring仅允许 tcp / quic
bandwidthLimit / bandwidthLimitModestring带宽上限及模式(client / server
allowUsersstring[]stcp/sudp/xtcp 允许访问用户,≤50 条
pluginCrtPath / pluginKeyPathstringHTTPS 插件证书 / 私钥路径

超限(单字段 >200 字符、配置 JSON >32KB)或含未知字段时整体拒绝。其余未列出的字段(versionremotePorttype 等)由服务端按表字段自动填充,客户端提交值以表字段为准。

secretConfigProxySecretConfig,仅出现在请求中):

字段类型说明
skstringstcp/sudp/xtcp 访问密钥(必填于这三类),≤128 字符且不得含控制字符
groupKeystring负载均衡组密钥,可选
httpUser / httpPwdstringhttp/https 基础认证用户名 / 密码,可选

请求示例

bash
curl -X POST https://api.hyperfrp.com/api/proxies \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "web-ssh",
    "type": "tcp",
    "local_ip": "127.0.0.1",
    "local_port": 22,
    "remote_port": 30001,
    "nodeId": 2,
    "use_encryption": true,
    "use_compression": false,
    "transport_protocol": "tcp",
    "comment": "内网 SSH"
  }'
javascript
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
const res = await fetch('https://api.hyperfrp.com/api/proxies', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer <token>',
  },
});
const data = await res.json();
console.log(data);
python
import requests

# 另一种用法:GET 
# 另一种用法:GET 
# 另一种用法:GET 
# 另一种用法:GET 
# 另一种用法:GET 
# 另一种用法:GET 
# 另一种用法:GET 
# 另一种用法:GET 
# 另一种用法:GET 
# 另一种用法:GET 
# 另一种用法:GET 
res = requests.post(
    'https://api.hyperfrp.com/api/proxies',
    headers={'Authorization': 'Bearer <token>'},
)
data = res.json()
print(data)
go
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
package main

import (
	"fmt"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.hyperfrp.com/api/proxies", nil)
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

校验流程与错误场景

服务端按以下顺序校验(都在同一事务内,行级锁保护)。注意:普通用户创建入口保持历史兼容,所有业务校验失败(含端口占用、域名冲突、配额已满等冲突类错误)统一返回 HTTP 400 + code: 400,并以 message 区分具体场景;HTTP 409 仅出现在更新 / 绑定域名 / 切换节点的冲突分支。

场景HTTP说明
请求体无法解析为 JSON400无效的请求数据
name / type / local_ip / local_port / nodeId 缺失或为零值400请填写所有必填字段:名称、类型、本地IP、本地端口、节点
名称不符合字符 / 长度规则;local_ip / comment / 证书路径超长400分别提示:隧道名称长度必须在 1-100 个字符之间隧道名称只允许包含字母、数字、下划线和短横线本地 IP 不能超过 255 个字符备注不能超过 500 个字符HTTPS 插件证书路径不能超过 255 个字符
local_port 越界400本地端口必须在 1-65535 之间
所选节点不存在400所选节点不存在
节点用户组白名单不含当前用户组400您当前的用户组无权使用节点 [节点名]
中国大陆节点未实名400使用中国大陆节点需要先完成实名认证
隧道配额已满(用户 / 用户组配额取大者)400隧道创建失败:隧道配额已满 (N个),无法创建更多隧道
type 不在枚举内400不支持的隧道类型:<值>。允许的类型为: tcp, udp, http, https, stcp, sudp, xtcp, tcpmux
节点配置了 allowedProtocols 且不含该类型400节点 [节点名] 不允许创建 <TYPE> 类型隧道
节点状态不可用(仅 online / api_only / unreachable 可建)400节点 '节点名' 当前不在服务状态,请选择其他节点
名称与当前用户既有隧道重名400已存在名为 '<名称>' 的隧道,请使用其他名称
transport_protocol 非法400传输协议只支持 tcp 或 quic
quic 但节点未开启 QUIC400所选节点未开启 QUIC,无法使用 QUIC 传输协议
proxy_protocol_version 与类型不匹配400TCP 类型仅支持 proxy protocol v1 或 v2 / UDP 类型仅支持 proxy protocol v2 / 仅 TCP 或 UDP 类型支持 Proxy Protocol
remote_port 越界400远程端口必须在 1-65535 之间
remote_port 不在节点允许的端口范围400远程端口 <N> 不在节点允许的端口范围内
remote_port 已被该节点其他隧道占用400远程端口 <N> 在节点 <节点名> 上已被占用,请更换
http/https/tcpmux 未提供 domains400HTTP/HTTPS 类型隧道必须指定绑定域名 / TCPMUX 类型隧道必须指定路由域名
域名格式无效 / 总长超 255400域名格式无效: <域名> / 自定义域名总长度不能超过 255 个字符
域名已被同节点其他隧道占用400域名 <域名> 已被节点 [节点名] 上的隧道 [隧道名] 占用
tcpmux 未提供 protocolConfig.multiplexer400TCPMUX 类型隧道必须配置 multiplexer(httpconnect)
stcp/sudp/xtcp 未提供 secretConfig.sk400STCP/SUDP/XTCP 类型隧道必须提供 sk 密钥
protocolConfig / secretConfig 含未知字段或超限400隧道权威配置格式无效或超出安全限制 / 隧道敏感配置格式无效或超出安全限制
敏感配置加密器不可用 / 加密失败500隧道敏感配置加密不可用,已拒绝操作 / 隧道敏感配置加密失败 / 创建隧道失败

响应字段

HTTP 201。data 为完整隧道模型(含预载 nodeuser 不返回),字段见「完整隧道字段」总表。新隧道初始态:enabledUser=truestatusAdmin=truestatus="stopped"secretConfigured / configurationComplete 按实际配置计算。

响应示例

json
{
  "code": 0,
  "message": "创建成功",
  "data": {
    "id": 42,
    "userId": 1001,
    "nodeId": 2,
    "node": { "id": 2, "name": "华南-广东-01", "host": "gd01.hyperfrp.com", "port": 7000 },
    "name": "web-ssh",
    "type": "tcp",
    "localIp": "127.0.0.1",
    "localPort": 22,
    "remotePort": 30001,
    "domain": "",
    "subDomain": "",
    "pluginCrtPath": "",
    "pluginKeyPath": "",
    "useEncryption": true,
    "useCompression": false,
    "transportProtocol": "tcp",
    "proxyProtocolVersion": "",
    "extraParams": "",
    "status": "stopped",
    "enabledUser": true,
    "statusAdmin": true,
    "adminReason": "",
    "trafficInBytes": 0,
    "trafficOutBytes": 0,
    "lastActiveAt": null,
    "comment": "内网 SSH",
    "createdAt": "2026-09-19T10:00:00+08:00",
    "updatedAt": "2026-09-19T10:00:00+08:00",
    "ccProtectionEnabled": false,
    "forceOfflineGeneration": 0,
    "configurationComplete": true,
    "secretConfigured": false
  }
}

注意事项

  • domains 字段虽然名字是复数,但类型是逗号分隔字符串,不是数组;提交数组会导致绑定失败。
  • 域名不支持 * 通配符(格式正则不允许),泛域名场景请逐个提交具体子域名。
  • remote_port 的节点允许范围判断优先级:节点配置了 allowPortRanges(多段)时必须在任一段内;否则按 allowedPortsStartallowedPortsEnd(两者均 >0 时);两者都未配置则不限制范围。随机端口接口(见下)的取值范围只按 allowedPortsStartEnd 计算,两者口径可能不同。
  • secretConfig 一经提交即 AES-GCM 加密落库;响应中只有 secretConfigured 布尔标记,sk 等明文不再可获取,请自行妥善保存。
  • cc_protection_enabled=true 且未提交规则时,服务端套用默认规则 {"rate":20,"burst":40} 并以 cc_rules 字段回显。

POST /api/proxies/batch-delete

批量删除当前用户名下的隧道。非本人或不存在的 ID 会被静默忽略(不报错),返回实际删除数量;仅当一条都删不到时返回 404。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求体

字段类型必填说明
idsnumber[]待删除的隧道 ID 列表,不得为空数组

请求示例

bash
curl -X POST https://api.hyperfrp.com/api/proxies/batch-delete \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "ids": [42, 43, 44] }'
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/batch-delete', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer <token>',
  },
  body: JSON.stringify({ "ids": [42, 43, 44] })
});
const data = await res.json();
console.log(data);
python
import requests

res = requests.post(
    'https://api.hyperfrp.com/api/proxies/batch-delete',
    headers={'Authorization': 'Bearer <token>'},
    json={
        'ids': [
            42,
            43,
            44
        ]
    },
)
data = res.json()
print(data)
go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	var payload bytes.Buffer
	json.NewEncoder(&payload).Encode(map[string]any{
		"ids": []any{
			42,
			43,
			44,
		},
	})

	req, _ := http.NewRequest("POST", "https://api.hyperfrp.com/api/proxies/batch-delete", &payload)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

字段类型说明
messagestring成功删除 N 个隧道(N 为实际删除数量,可能小于 ids 长度)

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": { "message": "成功删除 3 个隧道" }
}

错误场景

场景HTTP说明
ids 缺失或为空数组400请提供有效的隧道ID列表
列表中的 ID 均不属于当前用户(或不存在)404没有找到任何属于您的隧道进行删除,请检查ID是否正确
数据库删除失败500批量删除隧道失败

POST /api/proxies/batch-status

批量启用 / 禁用当前用户名下的隧道(写 enabledUser 开关)。非本人或不存在的 ID 静默忽略;一条都更新不到时返回 404。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求体

字段类型必填说明
idsnumber[]隧道 ID 列表,不得为空数组
enablebooleantrue 批量启用,false 批量禁用

请求示例

bash
curl -X POST https://api.hyperfrp.com/api/proxies/batch-status \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "ids": [42, 43], "enable": false }'
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/batch-status', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer <token>',
  },
  body: JSON.stringify({ "ids": [42, 43], "enable": false })
});
const data = await res.json();
console.log(data);
python
import requests

res = requests.post(
    'https://api.hyperfrp.com/api/proxies/batch-status',
    headers={'Authorization': 'Bearer <token>'},
    json={
        'ids': [
            42,
            43
        ],
        'enable': false
    },
)
data = res.json()
print(data)
go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	var payload bytes.Buffer
	json.NewEncoder(&payload).Encode(map[string]any{
		"ids": []any{
			42,
			43,
		},
		"enable": false,
	})

	req, _ := http.NewRequest("POST", "https://api.hyperfrp.com/api/proxies/batch-status", &payload)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

字段类型说明
messagestring成功启用 N 个隧道成功禁用 N 个隧道(N 为实际更新数量)

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": { "message": "成功禁用 2 个隧道" }
}

错误场景

场景HTTP说明
ids 缺失或为空数组400请提供有效的隧道ID列表
列表中的 ID 均不属于当前用户(或不存在)404没有找到任何属于您的隧道进行更新,请检查ID是否正确
数据库更新失败500批量更新隧道状态失败

注意事项

  • 无论启用还是禁用,本接口都会把 status 置为 stopped(启用后等待 FRPS 上报,回到 running 需要客户端重新连接)。
  • 禁用不会修改 statusAdmin(管理员侧开关);管理员禁用的隧道即使用户启用也无法运行。

端口与详情

GET /api/proxies/random-port/:nodeId

为指定节点随机取一个当前未被占用的远程端口,供创建隧道时预填。端口不会预留,创建时仍会做占用校验。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求参数(Path)

参数类型必填说明
nodeIdnumber节点 ID

请求示例

bash
curl https://api.hyperfrp.com/api/proxies/random-port/2 \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/random-port/2', {
  headers: {
    'Authorization': 'Bearer <token>',
  },
});
const data = await res.json();
console.log(data);
python
import requests

res = requests.get(
    'https://api.hyperfrp.com/api/proxies/random-port/2',
    headers={'Authorization': 'Bearer <token>'},
)
data = res.json()
print(data)
go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.hyperfrp.com/api/proxies/random-port/2", nil)
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

字段类型说明
portnumber随机可用端口

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": { "port": 30017 }
}

错误场景

场景HTTP说明
nodeId 非数字400无效的节点ID
节点不存在400节点未找到
节点端口范围配置不正确(起始 ≥ 结束)400节点的端口范围配置不正确,起始端口必须小于结束端口
连续 100 次随机均撞上已占用端口400尝试多次后仍未找到可用端口,请稍后重试或检查节点端口范围

注意事项

  • 取值范围来自节点的 allowedPortsStartallowedPortsEnd;未配置(≤0)时分别回退为 10000 与 65535。
  • 返回的端口只保证「调用时刻未被占用」,不写入任何预留记录;高并发下仍可能与其他用户创建冲突,以创建接口的校验结果为准。

GET /api/proxies/:id

获取单条隧道详情。仅能查看本人隧道;隧道不存在或属于他人时一律按 404 处理(权限隐藏,不区分两种情况)。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求参数(Path)

参数类型必填说明
idnumber隧道 ID

请求示例

bash
curl https://api.hyperfrp.com/api/proxies/42 \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/42', {
  headers: {
    'Authorization': 'Bearer <token>',
  },
});
const data = await res.json();
console.log(data);
python
import requests

res = requests.get(
    'https://api.hyperfrp.com/api/proxies/42',
    headers={'Authorization': 'Bearer <token>'},
)
data = res.json()
print(data)
go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.hyperfrp.com/api/proxies/42", nil)
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

data 为完整隧道模型(含预载 node,且已回填 secretConfigured / protocolConfig 虚拟字段),字段见「完整隧道字段」总表。

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": {
    "id": 42,
    "userId": 1001,
    "nodeId": 2,
    "node": { "id": 2, "name": "华南-广东-01", "host": "gd01.hyperfrp.com", "port": 7000, "domain": "gd01.hyperfrp.com", "location": "中国 广东 广州", "status": "online" },
    "name": "web-ssh",
    "type": "tcp",
    "localIp": "127.0.0.1",
    "localPort": 22,
    "remotePort": 30001,
    "domain": "",
    "useEncryption": true,
    "useCompression": false,
    "transportProtocol": "tcp",
    "proxyProtocolVersion": "",
    "status": "running",
    "enabledUser": true,
    "statusAdmin": true,
    "adminReason": "",
    "trafficInBytes": 1048576,
    "trafficOutBytes": 2097152,
    "comment": "内网 SSH",
    "ccProtectionEnabled": false,
    "forceOfflineGeneration": 0,
    "configurationComplete": true,
    "secretConfigured": false,
    "createdAt": "2026-09-19T10:00:00+08:00",
    "updatedAt": "2026-09-19T12:30:00+08:00"
  }
}

错误场景

场景HTTP说明
隧道不存在或不属于当前用户404隧道未找到或您无权访问
数据库查询失败500查询隧道失败

PUT /api/proxies/:id

更新本人隧道。字符串字段「空串 / 未提交 = 不修改」;布尔字段用指针语义「未提交 = 不修改」;提交 nodeId 且与现值不同时进入「切换节点」分支(此时其余字段全部忽略,只处理换节点)。更新成功后隧道 status 置为 stopped,等待客户端按新配置重连。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求参数(Path)

参数类型必填说明
idnumber隧道 ID

请求体

字段类型必填说明
namestring新名称。非空且与现值不同时校验:1–100 字符、仅字母 / 数字 / 下划线 / 短横线,且不得与本用户其他隧道重名
typestring切换隧道类型(转小写后校验枚举)。切换到 stcp/sudp/xtcp 时,若原隧道未设置过敏感配置且本次未提交 secretConfig,拒绝;切换到 tcpmux 时必须同时提交 protocolConfig.multiplexer
local_ipstring非空才更新
local_portnumber大于 0 才更新
remote_portnumber仅 tcp/udp/stcp/sudp 生效:大于 0 且与现值不同时做节点内占用校验(冲突返回 409);http/https 忽略;tcpmux 强制置 0
domainsstring仅 http/https/tcpmux 生效:非空且与现值不同时做域名冲突校验(冲突返回 409)
use_encryptionboolean布尔指针语义:未提交不修改
use_compressionboolean布尔指针语义:未提交不修改
transport_protocolstring非空才更新;仅 tcp / quic,且目标节点须开启 QUIC
proxy_protocol_versionstring非空才更新;tcp 仅 v1 / v2,udp 仅 v2,其他类型报错。类型改为非 tcp/udp 时自动清空
plugin_crt_path / plugin_key_pathstring仅 https 类型生效,非空才更新
cc_protection_enabledboolean布尔指针语义:未提交不修改
nodeIdnumber非零且与现值不同时切换节点(见下)
protocolConfigobject权威协议配置扩展字段,覆盖语义;未提交时沿用已保存值(不会丢失 locations / headers 等)
secretConfigobject显式替换敏感配置(结构同创建接口);未提交时保留原值
clearSecretConfigbooleantrue 显式清空敏感配置(优先于 secretConfig

切换节点分支nodeId 非零且变化)的校验顺序:目标节点存在(不存在返回 404)→ 目标节点状态须为 online / api_only / unreachable(否则 400)→ 用户组有权使用目标节点(否则 403)→ 原隧道的远程端口(tcp/udp/stcp/sudp)或域名(http/https/tcpmux)在新节点上不冲突(冲突返回 409)。切换节点时 name / type / 地址端口等其余字段不会被处理。

请求示例

bash
curl -X PUT https://api.hyperfrp.com/api/proxies/42 \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "local_port": 2222,
    "remote_port": 30002,
    "use_encryption": true
  }'
javascript
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
const res = await fetch('https://api.hyperfrp.com/api/proxies/42', {
  method: 'PUT',
  headers: {
    'Authorization': 'Bearer <token>',
  },
});
const data = await res.json();
console.log(data);
python
import requests

# 另一种用法:GET 
# 另一种用法:GET 
# 另一种用法:GET 
# 另一种用法:GET 
res = requests.put(
    'https://api.hyperfrp.com/api/proxies/42',
    headers={'Authorization': 'Bearer <token>'},
)
data = res.json()
print(data)
go
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
// 另一种用法:GET 
package main

import (
	"fmt"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("PUT", "https://api.hyperfrp.com/api/proxies/42", nil)
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

data 为完整隧道模型(含预载 node,已回填虚拟字段),字段见「完整隧道字段」总表。

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": {
    "id": 42,
    "name": "web-ssh",
    "type": "tcp",
    "localIp": "127.0.0.1",
    "localPort": 2222,
    "remotePort": 30002,
    "status": "stopped",
    "enabledUser": true,
    "statusAdmin": true,
    "configurationComplete": true,
    "secretConfigured": false,
    "updatedAt": "2026-09-19T13:00:00+08:00"
  }
}

错误场景

场景HTTP说明
请求体无法解析400无效的请求数据
隧道不存在或不属于当前用户404隧道未找到或您无权访问
名称 / 类型 / 传输协议 / Proxy Protocol 校验失败400消息同创建接口对应条目
切换到需要 sk 的类型但无密钥400STCP/SUDP/XTCP 类型隧道必须提供 sk 密钥
切换到 tcpmux 但缺 multiplexer / 路由域名400TCPMUX 类型隧道必须配置 multiplexer(httpconnect) / TCPMUX 类型隧道必须指定路由域名
名称与本用户其他隧道重名409您已存在名为 '<名称>' 的隧道,请使用其他名称
新远程端口在当前节点被占用409远程端口 <N> 在当前节点上已被隧道 [<名称>] 占用
新域名被同节点其他隧道占用409服务端返回的冲突描述(域名 <域名> 已被节点 [节点名] 上的隧道 [隧道名] 占用
目标新节点不存在404目标新节点不存在
目标新节点状态不可用400新节点 '<名称>' 当前不可用,无法切换
无权使用目标新节点403您当前的用户组无权使用新节点 [<名称>]
目标新节点上端口 / 域名冲突409远程端口 <N> 在新节点 [<名称>] 上已被隧道 [<名称>] 占用
QUIC 但节点未开启400当前节点未开启 QUIC,无法使用 QUIC 传输协议
已保存的 protocolConfig 损坏 / 版本不受支持400已保存的隧道权威配置无效,请重新提交 protocolConfig
持久化失败(内部错误)500更新隧道失败

注意事项

  • 本接口不支持修改备注 comment:请求体中没有该字段,备注仅在创建时可设置。
  • secretConfigclearSecretConfig 的组合语义:只提交 secretConfig = 替换;只提交 clearSecretConfig: true = 清空;两者都不提交 = 保留原值。
  • 普通字段与敏感密文、权威配置重建在同一事务内原子提交,任一步失败整体回滚,不会出现部分更新。

DELETE /api/proxies/:id

删除本人的一条隧道。隧道不存在或属于他人时按 404 处理(权限隐藏)。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求示例

bash
curl -X DELETE https://api.hyperfrp.com/api/proxies/42 \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/42', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer <token>',
  },
});
const data = await res.json();
console.log(data);
python
import requests

res = requests.delete(
    'https://api.hyperfrp.com/api/proxies/42',
    headers={'Authorization': 'Bearer <token>'},
)
data = res.json()
print(data)
go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("DELETE", "https://api.hyperfrp.com/api/proxies/42", nil)
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

字段类型说明
messagestring固定为 隧道已删除

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": { "message": "隧道已删除" }
}

错误场景

场景HTTP说明
隧道不存在或不属于当前用户404隧道未找到或您无权访问
数据库删除失败500删除隧道失败

状态与域名

PUT /api/proxies/:id/status

切换本人隧道的用户侧启用开关(enabledUser)。禁用时同时把 status 置为 stopped;启用不改变 status(等 FRPS 上报后回到 running)。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求体

字段类型必填说明
enablebooleantrue 启用,false 禁用

请求示例

bash
curl -X PUT https://api.hyperfrp.com/api/proxies/42/status \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "enable": false }'
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/42/status', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer <token>',
  },
  body: JSON.stringify({ "enable": false })
});
const data = await res.json();
console.log(data);
python
import requests

res = requests.put(
    'https://api.hyperfrp.com/api/proxies/42/status',
    headers={'Authorization': 'Bearer <token>'},
    json={
        'enable': false
    },
)
data = res.json()
print(data)
go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	var payload bytes.Buffer
	json.NewEncoder(&payload).Encode(map[string]any{
		"enable": false,
	})

	req, _ := http.NewRequest("PUT", "https://api.hyperfrp.com/api/proxies/42/status", &payload)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

字段类型说明
messagestring隧道已启用隧道已禁用
proxyobject更新后的完整隧道模型(字段见「完整隧道字段」总表;不预载 node

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": {
    "message": "隧道已禁用",
    "proxy": {
      "id": 42,
      "name": "web-ssh",
      "type": "tcp",
      "status": "stopped",
      "enabledUser": false,
      "statusAdmin": true
    }
  }
}

错误场景

场景HTTP说明
请求体无法解析400无效的请求数据
隧道不存在或不属于当前用户404隧道未找到或您无权访问
数据库保存失败500更新隧道状态失败

PUT /api/proxies/:id/bind-domain

为本人隧道绑定(更换)路由域名。仅 http / https 类型可用;tcpmux 类型需走更新接口。域名做冲突校验后以事务方式保存并重建权威配置,不会丢失既有扩展字段。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求体

字段类型必填说明
domainsstring新的绑定域名,英文逗号分隔的字符串(格式 / 长度规则同创建接口 domains

请求示例

bash
curl -X PUT https://api.hyperfrp.com/api/proxies/43/bind-domain \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "domains": "blog.example.hyperfrp.com,www.example.hyperfrp.com" }'
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/43/bind-domain', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer <token>',
  },
  body: JSON.stringify({ "domains": "blog.example.hyperfrp.com,www.example.hyperfrp.com" })
});
const data = await res.json();
console.log(data);
python
import requests

res = requests.put(
    'https://api.hyperfrp.com/api/proxies/43/bind-domain',
    headers={'Authorization': 'Bearer <token>'},
    json={
        'domains': 'blog.example.hyperfrp.com,www.example.hyperfrp.com'
    },
)
data = res.json()
print(data)
go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	var payload bytes.Buffer
	json.NewEncoder(&payload).Encode(map[string]any{
		"domains": "blog.example.hyperfrp.com,www.example.hyperfrp.com",
	})

	req, _ := http.NewRequest("PUT", "https://api.hyperfrp.com/api/proxies/43/bind-domain", &payload)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

字段类型说明
messagestring固定为 域名绑定成功
proxyobject更新后的完整隧道模型(字段见「完整隧道字段」总表;不预载 node

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": {
    "message": "域名绑定成功",
    "proxy": {
      "id": 43,
      "name": "web-blog",
      "type": "http",
      "domain": "blog.example.hyperfrp.com,www.example.hyperfrp.com",
      "enabledUser": true,
      "statusAdmin": true,
      "configurationComplete": true
    }
  }
}

错误场景

场景HTTP说明
请求体无法解析400无效的请求数据
隧道不存在或不属于当前用户404隧道未找到或您无权访问
隧道类型不是 http / https400只有 HTTP 和 HTTPS 类型的隧道才能绑定域名,当前隧道类型为 <TYPE>
域名已被同节点其他隧道占用409域名 <域名> 已被节点 [节点名] 上的隧道 [隧道名] 占用
域名格式无效409域名格式无效: <域名>(冲突检查入口以 Conflict=true 回传格式错误)
持久化失败(内部错误)500绑定域名失败 / 检查域名冲突失败

配置生成与客户端辅助

GET /api/proxies/:id/config-preview

生成本人隧道的 FRPC 客户端配置预览,同时返回 INI(legacy 格式)与 TOML(新格式)两份文本。生成前会做传输协议 fail-closed 校验;隧道名称含配置文件不安全字符时拒绝生成。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求示例

bash
curl https://api.hyperfrp.com/api/proxies/42/config-preview \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/42/config-preview', {
  headers: {
    'Authorization': 'Bearer <token>',
  },
});
const data = await res.json();
console.log(data);
python
import requests

res = requests.get(
    'https://api.hyperfrp.com/api/proxies/42/config-preview',
    headers={'Authorization': 'Bearer <token>'},
)
data = res.json()
print(data)
go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.hyperfrp.com/api/proxies/42/config-preview", nil)
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

字段类型说明
inistringINI 格式配置全文
tomlstringTOML 格式配置全文

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": {
    "ini": "[common]\nserver_addr = gd01.hyperfrp.com\nserver_port = 7000\nuser = \"AKxxxxxxxx\"\ntoken = \"<节点token>\"\n\n[web-ssh]\ntype = tcp\nmeta_tunnel_id = 42\nmeta_user_id = 1001\nlocal_ip = 127.0.0.1\nlocal_port = 22\nremote_port = 30001\nuse_encryption = true\n",
    "toml": "serverAddr = \"gd01.hyperfrp.com\"\nserverPort = 7000\nuser = \"AKxxxxxxxx\"\n\n[auth]\nmethod = \"token\"\ntoken = \"<节点token>\"\n\n[[proxies]]\nname = \"web-ssh\"\ntype = \"tcp\"\nmetadatas.tunnel_id = \"42\"\nmetadatas.user_id = \"1001\"\nlocalIP = \"127.0.0.1\"\nlocalPort = 22\nremotePort = 30001\n\n[proxies.transport]\nuseEncryption = true\n"
  }
}

生成内容结构说明

  • INI[common] 段(server_addr / server_port / user=用户访问密钥 / token=节点 FRPS token;QUIC 时附加 transport.protocol = "quic"),随后每个隧道一段 [<隧道名>]typemeta_tunnel_idmeta_user_id,非 https 类型输出 local_ip / local_port;tcp/udp 追加 remote_port,http/https 追加 custom_domains;https 类型改为插件形式(plugin = https2http + plugin_local_addr / plugin_crt_path / plugin_key_path,证书路径留空时输出占位提示文本);按需追加 use_encryption / use_compression / proxy_protocol_version
  • TOML:顶层 serverAddr / serverPort / user[auth]method = "token" + 节点 token),QUIC 时追加 [transport];每个隧道一个 [[proxies]] 块:name / type / metadatas.tunnel_id / metadatas.user_id / localIP / localPort / remotePort / customDomains(数组);https 类型追加 [proxies.plugin];加密 / 压缩 / Proxy Protocol 任一存在时追加 [proxies.transport]
  • 合并下载(见下)复用同一套生成器,[common] / 顶层只出现一次,其后串联该节点全部已启用隧道。

错误场景

场景HTTP说明
隧道不存在或不属于当前用户404隧道未找到或您无权访问
传输协议非法或节点未开启 QUIC400当前节点未开启 QUIC,无法使用 QUIC 传输协议 / 传输协议 "<值>" 不受支持,仅支持 tcp 或 quic
隧道名称含配置文件不安全字符500隧道名称不符合配置格式要求

注意事项

  • 配置中内嵌用户访问密钥(user)与节点 FRPS token(token),均为敏感凭据:预览文本请勿截图 / 转发到公开场合。
  • 隧道名称允许中文与点号(^[\p{L}\p{N}_.-]+$),但名称超长或含控制字符时生成器拒绝输出,需先改名。

POST /api/proxies/:id/quick-start-command

生成三平台的一键启动命令(使用官方 hyperfrpc 客户端拉起指定隧道)。命令内嵌用户访问密钥与面板 API 基址(取自节点配置并规范化,无尾斜杠);参数统一经单引号包裹,防 shell / PowerShell 注入。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求示例

bash
curl -X POST https://api.hyperfrp.com/api/proxies/42/quick-start-command \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/42/quick-start-command', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer <token>',
  },
});
const data = await res.json();
console.log(data);
python
import requests

res = requests.post(
    'https://api.hyperfrp.com/api/proxies/42/quick-start-command',
    headers={'Authorization': 'Bearer <token>'},
)
data = res.json()
print(data)
go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.hyperfrp.com/api/proxies/42/quick-start-command", nil)
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

字段类型说明
successboolean固定 true
commands.windowsstringWindows(PowerShell)启动命令
commands.linuxstringLinux 启动命令
commands.macosstringmacOS 启动命令

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": {
    "success": true,
    "commands": {
      "windows": "hyperfrpc.exe -u 'AKxxxxxxxx' -p 42 --api-url 'https://api.hyperfrp.com/api'",
      "linux": "./hyperfrpc -u 'AKxxxxxxxx' -p 42 --api-url 'https://api.hyperfrp.com/api'",
      "macos": "./hyperfrpc -u 'AKxxxxxxxx' -p 42 --api-url 'https://api.hyperfrp.com/api'"
    }
  }
}

错误场景

场景HTTP说明
隧道不存在或不属于当前用户404隧道未找到或您无权访问
用户访问密钥为空500无法获取用户的访问密钥 (Access Key)
隧道关联节点信息丢失500隧道关联的节点信息丢失
节点面板 API 地址无效400节点面板 API 地址无效

注意事项

  • 命令内嵌 accessKey(账号级凭据)明文:拿到命令即可用你的身份创建 / 修改隧道,请勿公开分享、截图或提交到代码仓库;泄露后应立即在控制台重置访问密钥(重置会使全部隧道强制下线)。
  • 每次调用都会写入审计日志(动作 tunnel-quick-start)。

POST /api/proxies/:id/force-unregister

对本人单条隧道执行强制下线:事务内原子递增持久强制下线代次(forceOfflineGeneration + 1)并把 status 置为 stopped,同时尝试发送 Redis 即时下线信号。代次是下线意图的事实来源——即使即时通知失败,FRPS 也会在下次状态检查时关闭该隧道。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求示例

bash
curl -X POST https://api.hyperfrp.com/api/proxies/42/force-unregister \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/42/force-unregister', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer <token>',
  },
});
const data = await res.json();
console.log(data);
python
import requests

res = requests.post(
    'https://api.hyperfrp.com/api/proxies/42/force-unregister',
    headers={'Authorization': 'Bearer <token>'},
)
data = res.json()
print(data)
go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.hyperfrp.com/api/proxies/42/force-unregister", nil)
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result)
}

响应字段

字段类型说明
successboolean固定 true
messagestring强制下线指令已发送成功;即时通知失败时追加 ;即时通知失败,已由持久下线记录兜底,将在节点下次状态检查时生效
forceOfflineFailedbooleantrue 表示 Redis 即时通知失败(持久代次已生效,仅延迟生效)

响应示例

json
{
  "code": 0,
  "message": "成功",
  "data": {
    "success": true,
    "message": "强制下线指令已发送成功",
    "forceOfflineFailed": false
  }
}

错误场景

场景HTTP说明
隧道不存在或不属于当前用户404隧道未找到或您无权访问
代次递增 / 状态更新失败500强制下线失败

配置下载

两个下载端点直接返回配置文件二进制附件(Content-Type: application/octet-stream + Content-Disposition: attachment),不走统一 JSON 包络;错误时仍返回 JSON 错误体。配置内容结构与预览接口一致(见上文「生成内容结构说明」),其中同样内嵌访问密钥与节点 token,下载后请妥善保管。

GET /api/proxies/config/download/:id/:format

下载本人单条隧道的 FRPC 配置文件。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求参数(Path)

参数类型必填说明
idnumber隧道 ID
formatstring配置格式:initoml,其他值报错

请求示例

bash
curl -o frpc_web-ssh.ini https://api.hyperfrp.com/api/proxies/config/download/42/ini \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/config/download/42/ini', {
  headers: {
    'Authorization': 'Bearer <token>',
  },
});
const blob = await res.blob(); // 浏览器可用 URL.createObjectURL(blob) 触发保存
python
import requests

res = requests.get(
    'https://api.hyperfrp.com/api/proxies/config/download/42/ini',
    headers={'Authorization': 'Bearer <token>'},
)
with open('frpc_web-ssh.ini', 'wb') as f:
    f.write(res.content)
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.hyperfrp.com/api/proxies/config/download/42/ini", nil)
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, _ := os.Create('frpc_web-ssh.ini')
	defer out.Close()
	io.Copy(out, res.Body)
	fmt.Println("saved frpc_web-ssh.ini")
}

响应说明

成功时返回 200 + 配置文件附件:

  • Content-Type: application/octet-stream
  • Content-Disposition: attachment; filename=frpc_<隧道名>.ini(或 .toml

错误场景

场景HTTP说明
隧道不存在或不属于当前用户404隧道未找到或您无权访问
format 不是 ini / toml400不支持的配置文件格式
传输协议非法或节点未开启 QUIC400同预览接口
隧道名称无法安全生成配置500隧道名称不符合配置格式要求

GET /api/proxies/config/download/merged/:nodeId/:format

下载本人指定节点下全部已启用隧道enabledUser=truestatusAdmin=true)合并而成的单文件 FRPC 配置,[common] / 顶层段只出现一次,其后串联每条隧道的配置块。

鉴权:JWT Bearer · 限流:无专用限流(全站 WAF 防护照常生效) · 缓存:NoCache(Cache-Control: no-store, no-cache, must-revalidate, private

请求参数(Path)

参数类型必填说明
nodeIdnumber节点 ID
formatstring配置格式:initoml,其他值报错

请求示例

bash
curl -o frpc_gd01_merged.toml https://api.hyperfrp.com/api/proxies/config/download/merged/2/toml \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch('https://api.hyperfrp.com/api/proxies/config/download/merged/2/toml', {
  headers: {
    'Authorization': 'Bearer <token>',
  },
});
const blob = await res.blob(); // 浏览器可用 URL.createObjectURL(blob) 触发保存
python
import requests

res = requests.get(
    'https://api.hyperfrp.com/api/proxies/config/download/merged/2/toml',
    headers={'Authorization': 'Bearer <token>'},
)
with open('frpc_gd01_merged.toml', 'wb') as f:
    f.write(res.content)
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.hyperfrp.com/api/proxies/config/download/merged/2/toml", nil)
	req.Header.Set("Authorization", "Bearer <token>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, _ := os.Create('frpc_gd01_merged.toml')
	defer out.Close()
	io.Copy(out, res.Body)
	fmt.Println("saved frpc_gd01_merged.toml")
}

响应说明

成功时返回 200 + 配置文件附件:

  • Content-Type: application/octet-stream
  • Content-Disposition: attachment; filename=frpc_<节点名>_merged.ini(或 .toml

错误场景

场景HTTP说明
节点不存在404节点未找到
该节点下没有已启用的隧道(enabledUserstatusAdmin 均为 true 才计入)404该节点下没有可用的隧道
各隧道传输协议不一致400隧道 "<名称>" 的传输协议与合并配置中其他隧道不一致(<A>/<B>),单个客户端只能使用一种协议
传输协议非法或节点未开启 QUIC400当前节点未开启 QUIC,无法使用 QUIC 传输协议
合并配置生成结果为空400合并配置无效,请检查隧道配置
查询节点 / 隧道列表失败500查询节点失败 / 获取隧道列表失败

注意事项

  • 单个 frpc 客户端的 transport.protocol 是全局的,因此合并配置要求该节点下所有已启用隧道的传输协议完全一致(全部 tcp 或全部 quic),混合即整体拒绝,不做静默降级。
  • 管理员禁用(statusAdmin=false)的隧道不会出现在合并配置中。

本站基于 VitePress 构建,文档内容以 backend-Go handler 源码为权威依据