手机里的一份 PDF 要放到电脑上,常见做法是先发给自己,再从聊天软件下载。文件少时没什么,换一台电脑、文件超过限制,或者不想让材料进聊天记录时,就想要一个更直接的入口。PairDrop 把设备发现和文件传输放在浏览器里,两端打开同一个网址,选择对方,接收后保存文件。
这里先部署一个供自己和家庭设备使用的入口,完成同一网络里的传输,再判断是否需要跨网络的 TURN。PairDrop 的接收设备需要在线,文件也需要在接收端保存。要让朋友过两天再来下载,应该使用有持久存储和分享管理的软件,不能只发给他一个 PairDrop 首页。
VPS 在传输中扮演什么角色
浏览器之间通常使用 WebRTC 传输,服务器负责页面和连接协调。能建立直接连接时,文件传输不需要绕着 VPS 的应用服务落盘;网络限制导致需要 TURN 中继时,文件流量会经过中继。若另行启用 WebSocket 回退,也要重新计算服务器承担的流量,不能把所有模式都说成“服务器不走流量”。
VPS 上的网页打开很快,并不能证明两端大文件一定传得快。速度还取决于发送设备、接收设备、Wi-Fi、浏览器以及实际连接路径。试用时先发一张图片和一个几 MB 的文件,再测试更大的文件,观察完成时间和接收结果。
PairDrop 提供设备配对等功能,适合避免每次都重新辨认设备。配对记录和设备名称有浏览器本地状态,清理网站数据或更换浏览器后需要重新检查。私人入口可以限制在 VPN 或自己的网络里;域名公开并不意味着它自带你预期的账号与权限体系。
一份简单的 Compose
服务器需要 Docker Engine、Compose 插件,以及能在宿主机上运行的 Caddy。域名示例 drop.example.com 解析到 VPS。先确认 3000 端口没有被其他服务占用,再建立目录。
mkdir -p /opt/pairdrop
cd /opt/pairdrop
docker version
docker compose version
保存为 compose.yaml。采用项目部署文档列出的 LinuxServer 镜像,应用端口只映射到回环地址;代理是客户端进入应用的唯一公网入口。PUID、PGID 是容器运行用户参数,需要按宿主机的用户安排,不是浏览器登录账号。
services:
pairdrop:
image: lscr.io/linuxserver/pairdrop:latest
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
environment:
PUID: "1000"
PGID: "1000"
TZ: Asia/Shanghai
RATE_LIMIT: "true"
WS_FALLBACK: "false"
RTC_CONFIG: "false"
DEBUG_MODE: "false"
本例保留 WebRTC 路径,关闭 WebSocket 回退,也没有自建 TURN。它可以用来完成基本部署和同一网络的传输验证,不能因此承诺所有手机蜂窝网络、公司网络之间都能传通。跨网络需求放到后面的连接验证中处理。
latest 用于首次拉取当前镜像。部署稳定后查看镜像摘要并固定,升级前检查项目和镜像发布记录。本例没有服务器端文件库挂载,不要误以为在 /opt/pairdrop 下面建一个 uploads 文件夹就会得到自动保存功能。
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=80 pairdrop
curl -I http://127.0.0.1:3000/
健康检查的第一步是上游能响应,再看域名入口。容器正常运行但本机 curl 失败时,检查端口绑定与应用日志;先不要围绕 DNS 反复改配置。修改环境变量后用 docker compose up -d 重建相应服务,单纯 restart 不会读取新的容器配置。
代理必须传递正确的客户端地址
PairDrop 需要代理设置正确的 X-Forwarded-For,设备发现和限流都会受到它影响。如果所有请求都被识别成代理地址,来自不同网络的访问可能被混在一起,限流也可能变成所有人共用一个额度。宿主机 Caddy 的 reverse_proxy 默认负责转发必要的头和 WebSocket,基本站点段如下。
drop.example.com {
reverse_proxy 127.0.0.1:3000
}
保存后验证配置、重载 Caddy,再访问 HTTPS 域名。如果域名前面还有 CDN 或另一个代理,应按实际链路配置受信任的代理来源,不能简单信任任何客户端送来的地址头。初次部署建议先让域名直接连接 Caddy,把单层代理跑通,再增加额外环节。
如果 Caddy 也运行在 Docker 里,这里的地址要改为共享网络上的服务名和端口。不要为了解决跨容器连不通而把应用端口直接暴露到公网,这会让访问绕过你刚设置的代理与访问限制。
HTTPS 除了保护入口,也关系到浏览器剪贴板、PWA 和一些持久状态功能。用 IP 加 HTTP 看到页面,只算完成应用启动,不能作为正式入口的验收。页面能打开而设备列表不断掉线时,查看 WebSocket 是否建立,检查中间代理有没有额外超时或拦截规则。
用两台自己的设备完成第一次传输
电脑和手机接入同一个 Wi-Fi,分别打开域名。先核对双方显示的设备名称,必要时改成容易辨认的名字。家庭成员同时在线时,不要凭一个默认动物名或相近图标猜接收者;先确认对方屏幕上的请求,再发送文件。
第一轮只发送一个无敏感信息的小文本。接收端接受后,下载或保存文件,打开核对内容。第二轮发送图片,确认分辨率与文件内容;第三轮才考虑较大的压缩包。对于重要文件,可以分别计算 SHA-256,比较传输前后是否一致,而不是仅看进度条到了 100%。
桌面 Linux 上可以使用下面的命令,替换成实际文件路径。其他系统使用相应的摘要工具。这个命令只做文件完整性核对,不证明文件本身安全。
sha256sum /path/to/file.zip
手机浏览器后台策略会影响长时间传输。发送过程中保持两端页面可用,避免锁屏后才发现任务已经中断。接收完成后确认文件保存在哪个目录,特别是移动端可能进入下载列表或系统文件应用;网页里的“完成”不能代替确认文件已保存。
配对之后还要验证断开和重连
长期一起用的设备可以按界面的配对流程建立关联,再刷新两端页面确认配对状态。配对码只发给预期接收者,不贴在公共讨论群里。设备借给别人或不再使用时,撤销对应配对,并检查浏览器是否留下历史状态。
跨网络前先把手机切到蜂窝数据,电脑保留原网络,重新打开双方页面。设备能发现但文件一直连接中,通常要继续检查 WebRTC 建连路径;首页访问与设备发现成功并不代表穿透已经成功。要稳定覆盖不同 NAT 和受限网络,应按项目部署说明配置 TURN。
TURN 需要独立的凭据、监听端口和中继端口范围,还涉及公网 IP 与防火墙。采用项目给出的 coturn 示例时,逐项配置这些条件,并确认 RTC 配置真正被应用读取。只有额外启动一个 coturn 容器,没有把配置交给 PairDrop 浏览器端,并不会自动获得中继能力。
如果网络里禁止 UDP,或者浏览器禁用了 WebRTC,还需要按具体限制选择方案。WebSocket 回退不是一个可以代替所有 TURN 配置的通用开关,启用前看项目当前支持范围,并测试它实际改变了哪条传输路径。项目说明明确指出,回退流量会经过服务端且可被服务端读取,因此只能使用自己信任的实例。采用服务器中继后,VPS 带宽和流量费用都应该重新计算。
常见现象各自怎么查
两端看不到对方,先确认访问的是同一实例、同一域名,页面没有停留在旧缓存中,再检查配对、网络和客户端地址识别。不要用两个不同站点的 PairDrop 页面测试“同一个服务”,即便界面看起来完全相同,它们也不会自动共享发现信息。
看到设备但发送失败,检查对方是否接受、页面是否仍在线,然后观察浏览器 WebRTC 连接与代理日志。小文件成功、大文件失败,除了网络,也要看浏览器内存、手机后台行为和接收端剩余空间。不要靠把服务器 HTTP 上传限制调到无限大来解决所有问题,文件未必通过那个 HTTP 上传路径。
所有人偶尔同时连接失败,检查代理传递的客户端地址和限流记录。本例打开 RATE_LIMIT,需要正确的代理识别作为基础。如果实际使用量超过默认限流范围,应在确认访问来源和需求以后调整,不能因为一次错误就永久关闭所有保护。
迁移入口时保存配置和真实连接需求
这个基础实例没有服务器端文件历史,迁移主要是保存 Compose、固定的镜像版本、域名代理配置和访问限制。以后如果加入 RTC 配置、TURN 凭据或其他挂载文件,它们都要一起备份,并按敏感信息管理。
cd /opt/pairdrop
mkdir -p /opt/backups
tar -czf "/opt/backups/pairdrop-config-$(date +%F-%H%M).tgz" compose.yaml
迁移前先让用户结束正在进行的传输,在新机器上启动并完成 HTTPS,再检查代理头和两端连接。保留原域名可以减少客户端入口变化,但浏览器配对记录能否继续工作仍要验证;不要把它当作服务器配置备份已经覆盖的内容。
安装之后,给家人保存一个固定入口,再附一句简单的使用说明:双方打开页面,核对设备,接收后保存文件。是否值得为跨网络传输增加 TURN,留给实际使用决定。先把经常发生的手机与电脑文件交换做好,这个服务就有明确的用途。