ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ttyd 网页终端共享故障排查指南:5 类常见报错速查定位

ttyd 网页终端共享故障排查指南:5 类常见报错速查定位

ttyd 网页终端共享故障排查指南:5 类常见报错速查定位

【免费下载链接】ttydShare your terminal over the web项目地址: https://gitcode.com/gh_mirrors/tt/ttyd

ttyd 是一个通过网页共享终端会话的轻量级命令行工具:本地敲一条命令,浏览器里就有一个完整的、支持中文输入和文件传输的终端。听起来省事,但真上线后,端口被占、SSL 握手失败、认证配错、长连接乱断,这些坑一个都少不了。下面把故障按"起不来 → 连不上 → 连上不可用 → 用久了变差"四个阶段收敛归类,每类收 2~3 个高频问题,每个都给可直接敲的命令和验证方法。先对号入座,再动手。

常见故障速查表

你看到的现象最可能原因跳转定位
stderr 打印Address already in use后进程退出7681端口被占用§1.1
远程访问http://IP:7681超时或拒绝连接防火墙 / 安全组没放行端口§2.1
反代后页面 404,终端空白base-path 没配-b§2.2
反复弹 Basic 登录框,输对了也不进密码含特殊字符 / 浏览器缓存旧凭据§2.3
能看输出,但敲键盘没反应默认只读,没加-W§3.1
终端窗口一闪就关启动命令不存在§3.2
新客户端连不上达到-m上限(预期行为)§3.3
放着不动过一会儿就断空闲 ping 超时§4.1

一、起不来:进程秒退或压根没监听 🔌

先看这一类——进程活不下来,后面的一切都谈不上。三个坑都集中在启动参数这一层,排查成本最低,优先从这里下手。

1.1 一句话定位端口占用

报错原文

bind: Address already in use

原因简析

libwebsockets 在绑定默认端口7681时,发现该端口已经被别的进程占了——可能是一次没退干净的旧 ttyd,也可能是完全不相干的服务。这类报错和 ttyd 本身没关系,纯粹是端口冲突。

解决命令

ss -tlnp | grep 7681

这条命令列出占用7681的进程 PID,先搞清楚"是谁占了"。

确认是旧的 ttyd 就干掉它:

kill -9 <PID>

<PID>换成上一步拿到的进程号,释放端口。

嫌麻烦的话直接换端口更省事:

ttyd -p 8080 bash

用-p指定新端口,绕开冲突。

如何验证

重新运行后,终端应打印Listening on port: 8080,浏览器打开http://localhost:8080能加载出终端页面,说明端口已正常监听。

1.2 启动参数写错,进程直接退出

报错原文

ttyd: invalid value for port: abcttyd: invalid credential, format: username:passwordttyd: missing start command

原因简析

这几条都来自 src/server.c 的main():getopt_long解析参数时,任何非法值(端口不是数字、credential 缺冒号、压根没给启动命令)都会fprintf(stderr, ...)然后立刻退出,进程根本进不到监听阶段。

解决命令

最常见两种:

ttyd -c admin:pass123 bash

用-c传用户名:密码,注意必须带冒号,ttyd -c admin bash这种缺冒号的写法会直接报 credential 错。

ttyd -p 8080 bash

确认命令写全——只写ttyd -p 8080而漏掉后面的bash,就会报missing start command。

如何验证

ttyd -h

这条命令打印完整帮助,对照每个选项的格式要求;再正式跑一次,看到Listening on port: ...即启动成功。

1.3 Windows 下 ConPTY 初始化失败

报错原文

ERROR: ConPTY init failed! Make sure you are on Windows 10 1809 or later.

原因简析

Windows 10 1809 之前的内核没有CreatePseudoConsole函数。src/pty.c 里conpty_init()通过动态加载kernel32.dll再查找该符号,找不到就返回 false,进程直接退出。这是系统版本问题,不是 ttyd 的 bug。

解决命令

升级操作系统到 Windows 10 1809 或更高版本,或换一台满足要求的机器。ttyd 侧没有可绕过的开关。

如何验证

重新运行后不再出现 ConPTY 报错,能看到Listening on port: 7681,说明 ConPTY 初始化通过。

进程能活下来之后,接下来最容易出现的问题是"进程明明在跑,浏览器却连不进来",这是下一类。

二、连不上:进程活着,浏览器打不开 🔗

服务端日志清清楚楚写着Listening on port: 7681,可就是访问不到。这一类九成不是 ttyd 的锅,而是它和浏览器之间的那条链路上,有人把路堵了。

2.1 防火墙或安全组挡了端口

报错原文 / 现象

服务端日志显示Listening on port: 7681,但远程浏览器访问http://IP:7681一直转圈,最后超时或"拒绝连接"。

原因简析

本地curl能通、远端不通,基本可以锁定在防火墙或云平台安全组上。端口在系统层被拦,和 ttyd 的配置无关。

解决命令

firewall-cmd --list-ports

查看当前放行的端口列表(firewalld 环境),确认7681在不在。

firewall-cmd --add-port=7681/tcp --permanent && firewall-cmd --reload

永久放行7681并重新加载规则,让配置生效。

如何验证

在客户端机器上执行:

curl -v http://IP:7681

返回 HTTP 200 并带出 HTML 内容,说明端口已真正打通。

2.2 反向代理 base-path 没配

报错原文 / 现象

Nginx 反代 ttyd 后,页面 404,或页面能加载但终端区域一直空白。

原因简析

走反代时请求路径带了前缀(比如/terminal/),而 ttyd 默认按根路径处理,于是返回 404;WebSocket 握手也因路径对不上被拒。src/server.c 里所有端点(/、/ws、/token)都要靠 base-path 统一加前缀。

解决命令

ttyd -b /terminal bash

用-b把 base-path 设成反代前缀,ttyd 会自动给所有端点补上该前缀。

如何验证

访问http://IP/terminal/能看到终端页面;打开浏览器 DevTools 的 Network 面板,看到/terminal/ws状态为101 Switching Protocols,即握手成功。

2.3 认证失败被反复弹登录框

报错原文(服务端日志)

WS authentication failed with token: ***

原因简析

src/protocol.c 里check_auth()做的是严格的字符串比对:把客户端Authorization: Basic <b64>头里Basic之后的部分,和-c传入的用户名:密码做 base64 后逐字节比较。密码里含冒号会破坏user:pass的解析,浏览器缓存了旧凭据也会让比对失败,于是反复弹框。

解决命令

ttyd -H X-Auth-User bash

用-H把认证交给反向代理:反代在请求头里带上X-Auth-User,ttyd 只校验该头是否存在,彻底绕开 Basic 认证的解析坑。

若仍用-c,先确认密码里没有:,并在无痕窗口里访问一次(清掉缓存的旧凭据)。

如何验证

无痕窗口访问一次即进入终端;服务端日志打印WS /ws - <client_ip>, clients: 1,说明连接已建立。

连上了,但终端用起来不对劲——能看到输出却敲不动、或窗口一闪就没,这是第三类。

三、连上不可用:页面开了,终端却不对劲 🖥️

网络通了、页面开了,但终端要么只读、要么闪退。这类问题最容易让人误以为"连上了就好了",实际上差一个参数或一个命令路径。

3.1 终端只读,敲键盘没反应

报错原文(启动日志警告)

The --writable option is not set, will start in readonly mode

原因简析

ttyd 默认是只读模式,src/server.c 的print_config()会主动打印这条警告。只读意味着能看输出,但INPUT消息会被直接丢弃(src/protocol.c 里if (!server->writable) break;),所以敲键盘没任何反应。

解决命令

ttyd -W bash

加-W开启可写。

如何验证

启动日志里不再出现 readonly 警告;浏览器终端里敲whoami、ls能正常执行并回显输出。

3.2 命令不存在,终端一闪而过

报错原文(终端窗口里)

bash: xxx: command not foundexecvp failed

原因简析

src/pty.c 的pty_spawn()用forkpty+execvp拉起命令。命令找不到时execvp返回 -1,子进程_exit(-errno),会话立刻结束,页面表现为"闪一下就关了"。

解决命令

which bash

确认命令在 PATH 里确实存在。要用不在 PATH 里的程序,写绝对路径:

ttyd -W /usr/bin/zsh

如何验证

终端稳定保持打开不闪退,执行echo $SHELL能返回预期的 shell 路径。

3.3 客户端超限,新连接被拒

报错原文(服务端日志)

refuse to serve WS client due to the --max-clients option.

原因简析

这是预期行为,不是故障。src/protocol.c 在连接过滤阶段检查client_count == max_clients,达到上限直接拒绝新连接,保护服务端不被压垮。

解决命令

ttyd -m 20 bash

按实际并发需求调大上限。如果上限是故意设的(比如做演示只想给少数人),那"被拒"本身就是正确结果,不用修。

如何验证

日志里clients:计数能涨到 20 而不被拒;第 21 个连接仍会被拒——这正符合-m 20的预期。

跑短期演示没问题,但要是挂着长期运行,时间一长就会暴露另一类更隐蔽的毛病。

四、用久了变差:跑得越久越不对劲 📉

这一类没有明显的报错,表现是"时好时坏"。多数和空闲超时、进程生命周期有关,优先怀疑网络层,再回头查 ttyd 参数。

4.1 长连接被空闲断开

报错原文 / 现象

终端放着不动,过一会儿突然断开,刷新才能重连。

原因简析

libwebsockets 的 retry 策略带secs_since_valid_ping(默认 5 秒)和secs_since_valid_hangup(默认 ping 间隔 + 7 秒),超时就主动断开;NAT 超时、WiFi 休眠也会让底层 TCP 悄悄断掉。这类"过一阵就断"通常是网络层心跳问题,不是 ttyd 自身。

解决命令

ttyd -P 10 bash

用-P把 WebSocket ping 间隔从默认 5 秒调到 10 秒(src/server.c 里retry.secs_since_valid_ping = interval),减少心跳包数量。若调了还频繁断,优先查网络而不是继续改 ttyd。

如何验证

ss -tlnp | grep 7681

确认监听正常;终端放置 30 分钟以上不再自动断开,客户端 DevTools 里 WS 连接状态保持 101。

4.2 客户端全退后进程还占着端口

报错原文 / 现象

所有浏览器都关了,ttyd 进程还在跑,端口一直被占着。

原因简析

默认行为就是"最后一个客户端断开后进程继续存活",方便复用。只有--once(只接一个客户端,断开即退)和--exit-no-conn(全部断开即退)才会主动退出(src/protocol.c 里LWS_CALLBACK_CLOSED处理)。

解决命令

ttyd -o bash

一次只接一个客户端,断开即退出,适合临时演示。

ttyd -q bash

所有客户端断开后退出,适合需要复用端口、按连接释放的场景。

如何验证

关闭所有浏览器标签后执行:

ps aux | grep ttyd

ttyd 进程消失;日志打印exiting due to the --once/--exit-no-conn option.,说明生命周期符合预期。

自查无果:收齐信息再找人,省一半时间

上面四类覆盖了绝大多数现场问题。如果你的情况没被覆盖,别急着问人——先把信息收齐再去找维护者或社区,来回追问才是最耗时间的。

先记下这几项

ttyd -v

拿到版本号,这是排查的第一手依据。

uname -a

记录操作系统与内核版本。

ttyd -d 1 bash

用-d 1把日志级别降到只输出错误(src/server.c 里-d设置 libwebsockets 日志级别,数值越小越安静),把完整 stderr 截下来。

再补上浏览器 DevTools 里 Console 和 Network 的报错截图,以及完整的启动命令(含所有参数)。

搜索关键词建议

  • 直接搜报错原文,如ttyd "Address already in use"、ttyd "WS authentication failed"
  • 加参数名搜,如ttyd -b base-path 404
  • 在官方 Issue 区搜同样报错,网络与认证类问题多数有现成答案

官方渠道

  • 项目 Issue 区:标题写清现象("连不上" / "认证失败"),正文贴全"版本 + 系统 + 启动命令 + 日志"四件套,别只写一句"不工作"
  • 社区讨论:描述里附上ttyd -h的输出,方便对方确认你当前版本支持的参数集

提 Issue 前的信息清单

  • ttyd -v版本号
  • uname -a系统输出
  • 完整启动命令
  • -d 1日志关键片段(20 行左右)
  • 浏览器名称 + 版本 + 控制台报错截图
  • 是否走反代 / 防火墙,若是附上反代配置片段

一份材料齐了再发出去,维护者基本能直接定位,省掉来回追问的功夫。

【免费下载链接】ttydShare your terminal over the web项目地址: https://gitcode.com/gh_mirrors/tt/ttyd

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进