Building a WinDbg Symbol Download Accelerator with CF Workers
该项目已在Tokisaki-Galaxy/cf-symbol-proxy开源,欢迎去点star。
Cloudflare Cache API 必须绑定自定义域名才能生效,*.workers.dev 域名不提供持久缓存。
告别 WinDbg 缓慢加载:为什么总是那么慢?
很多 Windows 开发者和安全研究员都遇到过 WinDbg 加载符号时卡住的情况。微软符号服务器 msdl.microsoft.com 在国内访问速度不稳定,下载一个 PDB 文件经常要等很久。
我开发了 cf-symbol-proxy 项目,利用 Cloudflare Workers 搭建一个轻量级符号代理。这样可以大幅提升下载速度,解决符号加载慢的问题。
为什么选择 Cloudflare Workers?
- 全球加速:利用 Cloudflare 的边缘节点,让请求在离你最近的地方处理。
- 免费额度:每天 10 万次免费请求,对于个人开发者完全足够。
- 边缘缓存 (Cache API):符号文件(PDB)具有不可变性(Immutable),一旦缓存,后续下载几乎是瞬时的。
- 零维护:无需购买服务器,无需担心运维。
亮点
针对微软符号服务器的特殊行为,做了深度优化:
修复 0 字节污染。微软服务器对 HEAD 请求会返回 200 OK,但内容为空。代理强制回源用 GET,确保缓存的是完整文件。
智能缓存。200 OK 缓存一年,404 缓存一小时,避免 WinDbg 频繁重试。
自动部署。集成 GitHub Actions,代码 push 后自动部署。
环境解耦。上游地址可通过 wrangler.toml 配置,支持多种符号源。
快速部署指南
你可以直接 Fork 我的仓库并快速上线自己的代理:
- (可选)Fork 仓库:访问 Tokisaki-Galaxy/cf-symbol-proxy。
- 配置自动构建:在 Cloudflare Workers 的控制台创建一个新 Worker,选择 GitHub 作为代码来源,连接到
https://github.com/Tokisaki-Galaxy/cf-symbol-proxy/,并设置自动部署。 - (可选)修改 wrangler.toml:将
upstream字段改为你想代理的符号服务器地址。
使用
一旦部署完成,你只需要在 Windows 环境变量中设置符号路径即可。
变量名:_NT_SYMBOL_PATH
变量值:
1 | |
碎碎念
在项目实现中,我遇到并解决了有点奇怪的问题,HEAD 请求与缓存键的冲突。
Cloudflare 的 Cache API 默认会根据请求的方法来处理。如果 WinDbg 发送了一个 HEAD 请求检查文件是否存在,而代理脚本简单地转发了这个请求,微软会返回一个 0 字节的响应。如果这个 0 字节响应被缓存,后续真正的 GET 下载请求也会拿到 0 字节。
解决方案是
1 | |
通过强制转换缓存键,确保了无论客户端发什么,我们存入边缘节点的永远是实实在在的文件数据。
This project is open-sourced at Tokisaki-Galaxy/cf-symbol-proxy. Feel free to give it a star!
The Cloudflare Cache API requires a custom domain to function. The default *.workers.dev domains do not support persistent caching.
Say Goodbye to Slow WinDbg Loading: Why is it always so slow?
Many Windows developers and security researchers have experienced WinDbg hanging while loading symbols. The Microsoft Symbol Server (msdl.microsoft.com) can be highly unstable in certain regions (like China), and downloading a single PDB file often takes an eternity.
I developed the cf-symbol-proxy project to build a lightweight symbol proxy using Cloudflare Workers. This significantly boosts download speeds and resolves symbol loading latency.
Why Choose Cloudflare Workers?
- Global Acceleration: Leverage Cloudflare’s edge nodes to process requests at the location closest to you.
- Generous Free Tier: 100,000 free requests per day, which is more than enough for individual developers.
- Edge Caching (Cache API): Symbol files (PDBs) are immutable. Once cached, subsequent downloads are nearly instantaneous.
- Zero Maintenance: No need to buy servers or worry about DevOps.
Highlights
Deeply optimized for the specific behaviors of the Microsoft Symbol Server:
- Fixing 0-byte Pollution: Microsoft servers often respond to
HEADrequests with a200 OKbut empty content. The proxy forces the origin fetch to useGETto ensure the cache contains the complete file. - Smart Caching:
200 OKresponses are cached for one year, while404 Not Foundresponses are cached for one hour to prevent WinDbg from frequently retrying missing files. - Automatic Deployment: Integrated with GitHub Actions; code is automatically deployed upon pushing.
- Environment Decoupling: The upstream address can be configured via
wrangler.toml, supporting various symbol sources.
Quick Deployment Guide
You can directly fork my repository to launch your own proxy:
- (Optional) Fork the Repo: Visit Tokisaki-Galaxy/cf-symbol-proxy.
- Configure Auto-Build: In the Cloudflare Workers dashboard, create a new Worker, select GitHub as the source, connect to your repository, and set up automatic deployment.
- (Optional) Modify
wrangler.toml: Change theupstreamfield to the symbol server address you wish to proxy.
Usage
Once deployed, you simply need to set your symbol path in the Windows Environment Variables.
Variable Name: _NT_SYMBOL_PATH
Variable Value:
srv*D:\Symbols*https://your-custom-domain.com
Technical Ramblings
During implementation, I encountered a peculiar issue: The conflict between HEAD requests and cache keys.
The Cloudflare Cache API treats requests based on their HTTP method by default. If WinDbg sends a HEAD request to check if a file exists and the proxy script simply forwards it, Microsoft returns a 0-byte response. If this 0-byte response gets cached, subsequent legitimate GET requests for the download will also receive 0 bytes.
The solution was:
// Force the use of GET to construct the cache key
const cacheKey = new Request(url.toString(), { method: 'GET' });
By forcing the cache key to always use GET, we ensure that regardless of what the client sends, what we store in the edge node is always the actual file data.