code-server 把 VS Code 搬到了浏览器里,是很多人做云端开发的首选方案。但它对网络质量非常敏感:一旦代理链路出现丢包或延迟抖动,编辑器的自动补全、终端输出就会明显卡顿。QUIC 协议天生就是为了解决这类问题而设计的,它在用户态实现可靠传输,握手快、抗丢包、连接迁移能力强。Apache 从 2.4.x 后期版本开始通过 mod_http3 模块提供 HTTP/3 支持,配合 mod_cache 和 mod_proxy,可以搭建一套体验相当不错的 code-server 访问入口。这篇文章就把整套配置过程和背后的原理讲清楚。

一、为什么 code-server 适合走 HTTP/3
先看问题本质。code-server 的流量有两个鲜明特点:一是长连接多,WebSocket 承载着终端、文件监听、扩展通信等多条通道;二是小包密集,每一次按键、每一次补全请求都是一次小体积的网络往返。在传统的 HTTP/1.1 反向代理下,浏览器需要为并发请求建立多条 TCP 连接,而 TCP 的队头阻塞会让一个丢包拖慢所有连接上的请求。HTTP/2 虽然做了多路复用,但复用发生在 TCP 之上,TCP 层的队头阻塞依然存在。
QUIC 直接把传输层搬到了 UDP 上,在用户态实现了自己的可靠传输和流控。QUIC 的流之间相互独立,某个流丢包只会阻塞它自己,其他流照常收发。对于 code-server 这种请求密集且小包为主的场景,这个特性收益非常直接。此外,QUIC 的 1-RTT 甚至 0-RTT 握手,能让用户在频繁打开新标签页访问工作区时几乎感觉不到建连开销。
还需要说明一点:code-server 本身基于 Node.js,原生并不支持 HTTP/3 监听,所以正确的做法是让 Apache 在前面终结 HTTP/3,后端仍然通过传统的 HTTP/1.1 与 WebSocket 转发给 code-server。也就是说,QUIC 只作用在用户浏览器到 Apache 这一段,这一段恰恰是公网质量最不可控的一段,因此收益最大。
二、编译并启用 Apache 的 mod_http3 模块
目前主流发行版的 Apache 包还没有默认附带 mod_http3,需要自己编译。mod_http3 依赖 ngtcp2 和 ngtcp2 项目族的 QUIC 实现,编译步骤不算复杂,但要严格按顺序安装依赖。以下命令在 Debian 系发行版上验证通过:
# 安装编译依赖
apt-get install build-essential libssl-dev cmake ninja-build \
libevent-dev nghttp3-dev libngtcp2-dev libngtcp2-crypto-openssl-dev
# 获取 mod_http3 源码并编译
git clone https://github.com/icing/mod_http3.git
cd mod_http3
./configure --with-apxs=/usr/bin/apxs
make && make install编译完成后,在 Apache 配置目录中加载模块。注意 mod_http3 要求 mod_http2 已经启用,因为 HTTP/3 的连接发现依赖 HTTP/2 响应头里的 Alt-Svc 字段。可以这样启用:
a2enmod http2
a2enmod proxy proxy_wstunnel cache cache_disk ssl
# 手动加载 http3(部分版本无 a2enmod 脚本)
echo "LoadModule http3_module /usr/lib/apache2/modules/mod_http3.so" \
> /etc/apache2/mods-available/http3.load
a2enmod http3这里有个容易踩的坑:ngtcp2 的版本必须和 mod_http3 匹配,版本过旧会在启动时报 undefined symbol 错误。如果遇到 Apache 启动失败,先检查 error.log 中的模块加载信息,必要时从源码编译 ngtcp2 并指定 PKG_CONFIG_PATH 重新编译 mod_http3。另外,QUIC 走 UDP 协议,防火墙和云服务商的安全组必须放行 UDP 443 端口,这是最常见的配置遗漏。
三、配置 HTTP/3 监听与 code-server 反向代理
模块加载完成后,核心工作就是写好虚拟主机配置。下面是一份可直接参考的完整配置,包含了 HTTP/3 监听、TLS 证书、Alt-Svc 通告以及 code-server 的代理规则:
Listen 443
Protocols h3 h2 http/1.1
<VirtualHost *:443>
ServerName dev.bbccb.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/dev.bbccb.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/dev.bbccb.com/privkey.pem
# 开启 HTTP/3 并通告端口
ProtocolsH3 on
Header always set Alt-Svc "h3=\":443\"; ma=86400"
# 代理 code-server,保留 WebSocket 升级能力
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:8080/ upgrade=websocket
ProxyPassReverse / http://127.0.0.1:8080/
# 静态资源缓存,扩展和资源文件更新频率低
<LocationMatch "\.(js|css|woff2|png|svg|wasm)$">
CacheEnable disk
Header set Cache-Control "public, max-age=604800"
</LocationMatch>
</VirtualHost>配置里有几处细节值得展开。第一,Protocols h3 h2 http/1.1 这一行决定了协商顺序,浏览器会优先尝试 h3,失败后自动回退,所以即使某个客户端不支持 QUIC 也不会影响访问。第二,Alt-Svc 响应头是浏览器发现 HTTP/3 的关键,没有它浏览器永远不会主动尝试 QUIC,可以用 curl -I 检查响应头是否生效。
第三,code-server 的 WebSocket 代理必须使用 upgrade=websocket 参数,否则终端和实时协作功能会直接失灵,表现为页面能打开但终端连不上。第四,缓存这块要克制:code-server 的主页面 HTML 和 API 请求绝不能缓存,只对带哈希文件名的静态资源设置长缓存,否则更新扩展后浏览器可能拿到旧文件,出现白屏或脚本报错。
四、缓存策略设计与效果验证
code-server 的静态资源体积不小,首次加载往往要传输十几兆的 JS 和 wasm 文件。用 mod_cache 做磁盘缓存后,Apache 可以直接从本地磁盘返回这些文件,省去每次都回源到 Node.js 进程的开销。配合 CacheDirLength 和 CacheDirLevels 可以控制缓存目录结构,避免单目录文件过多:
# 缓存全局参数,放在虚拟主机外 CacheRoot /var/cache/apache2/mod_cache_disk CacheDirLevels 2 CacheDirLength 1 CacheMaxFileSize 50000000 CacheIgnoreNoLastMod On
验证 HTTP/3 是否真正生效,最直接的方法是打开浏览器的开发者工具,在 Network 面板的协议列中查看是否显示 h3。命令行下也可以用支持 HTTP/3 的 curl 构建:curl --http3-only -v https://dev.bbccb.com,如果能看到 QUIC 握手日志说明链路已经打通。缓存效果则可以通过对比两次请求的响应时间,或者查看 apachectl mod_cache_disk 相关统计,也可以直接观察缓存目录下的文件增长情况。
最后做一次小结性的权衡分析。这套方案的代价是维护成本:mod_http3 还在快速迭代,升级 Apache 或 OpenSSL 时可能需要重新编译模块;收益则是在弱网环境下 code-server 的交互延迟明显降低,尤其是移动网络下终端操作不再断断续续。如果你的团队经常在咖啡厅、高铁上写代码,这套架构值得投入。如果全部访问都发生在内网机房,那么 HTTP/2 加磁盘缓存就已经足够,不必为了 QUIC 增加运维负担。
Apache HTTP/3QUICcode-server修改时间:2026-09-16 05:45:40