Hotdry.

Article

URL 极限压缩与整站直出:Nowhere 的五步编码 pipeline 工程实践

深入解析 Nowhere 项目如何将完整网站压缩至 URL fragment 中,包含五步编码 pipeline 细节与 data:URL 方案的性能对比。

2026-04-24web

当我们谈论「把网站装进 URL」时,大多数人首先想到的是 data:URL—— 将图片、样式表或小脚本 base64 编码后直接嵌入 HTML。这种方式早已广泛使用,但它本质上是一种资源内联策略,而非真正的「整站即 URL」。Nowhere 项目走得更远:它把整个网站的结构、内容、交互逻辑全部压缩进 URL fragment,不依赖任何服务器存储,访问者打开链接的那一刻,网站从浏览器地址栏中「生长」出来。这不是概念演示,而是一套完整的工程化方案,涉及精细的序列化、字典替换、压缩算法选型和加密层设计。

URL fragment:永不触及服务器的天然容器

理解 Nowhere 的技术起点,需要先弄清 URL 的 fragment 机制。URL 由协议、主机、路径、查询字符串和 fragment(# 后面的部分)组成。HTTP 规范明确规定:fragment 不会随请求发送到服务器。它停留在浏览器端,仅供 JavaScript 读取。这一设计初衷是为了页面锚点跳转,但被 Nowhere 改造为隐式的数据通道。

当用户打开一个 Nowhere 链接时,浏览器向服务器请求的只是一个通用的静态页面构建器文件。对服务器而言,它无法区分访问者是在浏览一家书店、一个论坛还是一份请愿书 —— 因为 fragment 部分压根不在请求中。这是 HTTP 规范赋予的隐私特性,也是整个架构的根基。

五步编码 pipeline:极限压缩的工程细节

将一个完整的网站(商品列表、论坛帖子、活动信息)压缩到可分享的 URL 长度,是一项严格的工程挑战。Nowhere 采用五步 pipeline,每一步都针对特定的数据特征进行优化。

第一步是序列化(Serialize)。 原始数据被转换为紧凑的文本表示。可选字段完全省略,布尔值被压缩进 base64url 位字段,字段之间使用分隔符而非键值对形式。这一步的目标是:在不丢失任何信息的前提下,用最少的字符数表示数据结构。

第二步是字典替换(Substitute)。 这是一个关键优化环节。Nowhere 预置了一个包含常见词汇、短语、URL 前缀和 emoji 的替换字典。频繁出现的词被替换为一到两个字符的代码。字典替换在压缩算法之前执行,针对英文文本可以显著减少字符总量。这个思路类似于 DNA 压缩中的替换表,但针对 Web 内容做了专门调优。

第三步是 LZ77 压缩。 使用 pako 库的 deflateRaw 函数,其内部首先执行 LZ77 算法。LZ77 扫描数据流中的重复字节序列,用「长度 + 距离」的回引指针替换重复部分。例如,字符串「Nowhere uses Nostr relays」中重复出现的「re」会被替换为指向更早位置 的回引。LZ77 是 gzip 和 zip 内部使用的第一层压缩,它的优势在于对重复结构有天然的敏感性。

第四步是霍夫曼编码(Huffman)。 LZ77 的输出继续经过霍夫曼编码。每个符号根据出现频率被分配变长编码:常见符号获得更短的编码,罕见符号获得更长的编码。LZ77 与霍夫曼共同构成原始 DEFLATE 算法 —— 这正是 gzip 和 zip 文件使用的底层压缩方式,但 Nowhere 去除了所有包装头部,只保留纯压缩数据。

第五步是 base64url 编码。 压缩后的二进制流被转换为 URL 安全的 base64url 字符集,不需要额外的百分号编码,可以直接放在 fragment 中。

解码过程完全相反:base64url 转字节 → inflateRaw → 恢复替换 → 反序列化。整个编解码过程都在浏览器中完成,不涉及服务端计算。

与 data:URL 的本质差异

理解 Nowhere 的技术定位,需要与传统的 data:URL 方案做出清晰区分。data:URL 将资源(通常是图片、字体或内联 CSS/JS)以 base64 形式嵌入 HTML 文档本身。它的使用场景是「减少 HTTP 请求数」或「实现单文件分发」,但它不改变数据存储方式 —— 内容仍然需要通过某个 HTML 文件作为载体。

Nowhere 的做法是彻底移除 HTML 文件这一层。整个网站的数据就是 URL 本身,访问时只需要加载一个与具体内容无关的静态解码器。data:URL 解决的是「资源内联问题」,而 Nowhere 解决的是「无服务器分发问题」。前者的数据随 HTML 存在,后者的数据随链接存在。这是一个质的变化:链接即网站,网站即链接。

在压缩效率上,两者不可直接比较,因为处理的数据类型不同。但可以指出一个关键差异:data:URL 直接 base64 编码原始数据,没有中间的压缩步骤;Nowhere 则在 base64 之前经历了 LZ77 和霍夫曼的两层压缩。对于文本内容占主导的网站(商店、论坛、文档),Nowhere 的压缩比远优于纯 base64。根据 Nowhere 官方描述,一个完整的商店(包括商品目录、描述、定价、运费规则、退换政策)可以被压缩到一个可分享的 URL 中。

整站直出的技术参数与工程阈值

将整站数据塞入 URL 面临若干实际的工程约束,理解这些参数有助于评估该方案的适用边界。

URL 长度实际限制。 业界长期存在「URL 最大长度 2083 字符」的误解,这来自旧版 Internet Explorer 的限制。现代浏览器支持的 URL 长度远大于此:Chrome 和 Firefox 支持数万字符,Safari 甚至更高。但从用户体验角度,超过 2000 字符的 URL 在复制粘贴、QR 码生成和分享时会出现问题。Nowhere 的目标是在可用的长度范围内实现最大信息密度,五步压缩 pipeline 直接服务于这一目标。

压缩率的经验参数。 对于典型的商品列表或论坛结构,序列化 + 字典替换 + DEFLATE 压缩通常能达到 5:1 到 10:1 的压缩比。这意味着 50KB 的结构化数据可以压缩至 5KB-10KB 区间,落在可分享的范围内。但这个比率高度依赖内容特征:高度重复的结构、常见词汇占比高的描述会获得更好的压缩率;随机性强的内容(加密数据、随机生成的 ID)压缩效果较差。

解码性能要求。 解码过程涉及 base64 解码、DEFLATE 解压、字典恢复和反序列化。在主流设备上,这个过程对于几 KB 到十几 KB 的数据通常在几十毫秒内完成。但当数据量接近 URL 长度上限时,解压时间可能超过 200ms,对交互体验产生可感知的影响。Nowhere 通过将解码逻辑尽可能精简来缓解这一问题,但开发者仍需意识到这是一个需要权衡的因素。

QR 码容量约束。 如果 URL 需要转化为 QR 码传播,字符长度直接决定 QR 码的密度和可扫描距离。约 1000 字符以下的 URL 可以生成相对容易扫描的 QR 码;超过 2000 字符后,QR 码变得密集且容易识别失败。Nowhere 建议将内容精简到可生成可用 QR 码的长度范围内,否则需要考虑分块或混合方案。

静态与动态的架构分层

Nowhere 将功能划分为两个清晰的层次。五个纯静态工具(消息、活动、Drop、作品、募捐)完全由 URL fragment 定义,访问时除了加载解码器,不需要任何网络请求。即使设备离线,只要解码器已缓存或可本地部署,网站依然可以正常渲染。这是该架构最极端的形态:真正的「无服务器、无网络」访问。

对于需要实时交互的功能(订单提交、请愿签名、论坛发帖),Nowhere 引入 Nostr relays 作为通信基础设施。这里的工程设计非常精巧:所有发送到 relay 的内容都经过 NIP-44 加密,relay 只能看到一串无法解读的密文;每次操作使用一次性密钥对,操作完成后立即丢弃;relay 无法识别内容属于哪个网站、哪个论坛,因为 topic 标签是通过 HMAC-SHA256 从 URL fragment 派生出来的。

这种分层设计的工程意义在于:静态内容享受最大程度的去中心化和抗审查能力,动态交互则利用现有 relay 基础设施而不必自建服务器。两者在数据流上是解耦的,静态部分的可靠性不依赖 relay 的可用性。

何时考虑这种架构

URL 极限压缩方案并非万能药,它适合特定的使用场景。需要同时满足以下条件时,Nowhere 式的整站直出架构值得考虑:内容更新频率低(发布后基本不变)、目标受众的访问渠道多元(可能需要离线或打印分发)、对抗审查有强需求(无法被平台下架)、单次分享的内容量可以控制在数 KB 级别。

不适合的场景包括:内容频繁更新的大型网站、需要复杂服务端逻辑的应用、对加载性能极度敏感且数据量可能超过数十 KB 的情况。在这些场景下,传统的服务器托管或 CDN 分发方案仍然是更务实的选择。

资料来源:Nowhere 官方网站(hostednowhere.com)及其技术实现文档(how-it-works 页面)。

web