
1. 为什么今天还要学 Charles它不是“老古董”而是调试现场的战术手电Charles 这个名字在很多刚入行的开发者嘴里常被当成“前辈们用的老工具”“抓包时代遗留物”甚至有人直接说“现在都用 Chrome DevTools 了谁还装 Charles”。我第一次听到这种说法是在某次技术分享后的茶歇一位刚转行做前端的 A 同学边喝咖啡边问“老师Charles 现在还有必要学吗我看文档都快十年没大更新了。”我当时没急着回答而是掏出手机连上公司测试 Wi-Fi打开一个正在灰度中的电商小程序——三秒内用 Charles 拦截并重写了一笔模拟优惠券核销请求把原价 299 的商品临时改成 0.01 元然后刷新页面订单页立刻显示“支付成功”。A 同学盯着屏幕愣了两秒说“这……比 Fiddler 快比浏览器 Network 面板准还能改”这就是 Charles 的真实定位它从来不是通用型“网络监控器”而是一把专为调试、复现、验证、逆向验证逻辑边界而生的战术手电。它的价值不在于“能看多少流量”而在于“能在哪一层、以多细的粒度、多稳地干预哪一类请求”。尤其在混合开发WebView 原生、小程序调试、App 后端联调、第三方 SDK 行为审计、HTTPS 流量可控重放等场景中它仍是不可替代的“最后一道可控入口”。核心关键词——Charles、HTTPS 抓包、请求重写、断点调试、Map Local、SSL 代理——每一个都不是孤立功能而是环环相扣的调试链路。比如“Map Local”表面是“把线上接口换成本地 JSON”实则背后依赖 SSL 代理建立可信通道、依赖域名匹配规则精准路由、依赖响应头自动继承机制避免 CORS 破坏前端流程。你不用它可能只是因为还没遇到那个必须“让服务器返回一个特定错误码来触发弹窗”的 QA 场景或者还没碰上“iOS App 在某个特定证书链下无法加载图片”的疑难杂症。适合谁读不是只给“要配证书的安卓测试工程师”而是给所有需要在非生产环境里对任意 HTTP(S) 流量做确定性干预的人前端要验证异常状态 UI后端要复现客户端传参 bug测试要构造边界数据组合安全同学要审计第三方 SDK 的上报行为甚至产品同学想快速验证某个接口字段变更对页面的影响——只要你的工作流里存在“我想让这个请求变成那样”Charles 就值得你花 45 分钟真正掌握它而不是停留在“装了但只会看 Headers”。2. Charles 的底层设计逻辑为什么它敢叫“HTTP 代理调试工具”而不是“抓包软件”2.1 它不是被动监听而是主动代理从架构上理解“可控性”来源很多人误以为 Charles 是像 Wireshark 那样“监听网卡流量”这是根本性误解。Charles 的本质是一个正向 HTTP/HTTPS 代理服务器Forward Proxy它不监听物理层或传输层而是要求客户端显式地把网络请求发给它。这个“显式”动作就是一切可控性的起点。举个生活化类比Wireshark 是在高速公路边架设高清摄像头拍下所有经过车辆的车牌和车型而 Charles 是在路口设了一个收费站所有车必须先停到它面前交费即发送请求、接受检查查看/修改请求、再被放行转发给真实服务器。收费站可以拦下某辆特定牌照的车按域名过滤可以临时换掉它的货物清单重写请求体可以假装前方道路封闭让它原路返回返回自定义响应甚至可以复印一份它的过路单存档保存会话供回放——这些操作摄像头永远做不到。所以 Charles 的第一步永远是“配置客户端走代理”iOS/Android 设备在 Wi-Fi 设置里手动填入电脑 IP 和 Charles 默认端口8888浏览器通过系统代理或插件强制走 Charles桌面 App部分支持代理设置或需配合系统级代理如 macOS 的 networksetup 命令小程序/WebView需在代码中显式设置WebView.getSettings().setProxy(...)或使用调试模式。提示Charles 默认只监听 localhost127.0.0.1若要让手机访问必须在Proxy → Proxy Settings → Access Control Settings中勾选Allow remote connections并确认防火墙放行 8888 端口。我见过太多人卡在这步反复检查手机 DNS、IP 是否正确最后发现是 Mac 防火墙默默拦截了入站连接。2.2 HTTPS 解密不是“破解”而是“中间人信任体系重建”这是 Charles 最常被问、也最容易出错的核心机制。当你说“Charles 能抓 HTTPS”实际发生的是手机/电脑将https://api.example.com请求发给 Charles代理Charles 以自己身份向api.example.com发起新的 HTTPS 请求拿到真实证书和响应Charles动态生成一张伪造证书主题名Subject CN与api.example.com完全一致但签发者是 Charles Root CertificateCharles 把这张伪造证书 真实响应一起发回给客户端客户端校验证书时发现签发者不在系统信任库——于是报“不安全连接”解决方案不是让 Charles “绕过验证”而是让用户设备“信任 Charles 的根证书”。这个过程的关键在于Charles 并未破解 TLS 加密算法它只是利用了 HTTPS 信任链的可配置性。就像你家小区门禁系统保安Charles不会黑进物业数据库但他可以给你一张由“本小区物业总公司”Charles Root CA签发的临时门禁卡只要你把这张总公司的“营业执照复印件”Root Certificate贴在门卫室墙上安装到系统信任库他就能合法放行。因此“安装 Charles Root Certificate”绝不是点一下“始终信任”就完事。在 iOS 上需进入设置 → 已下载描述文件 → 安装 → 通用 → 关于本机 → 证书信任设置 → 开启 Charles Proxy CA在 Android 7需将证书放入系统证书目录非用户目录否则 OkHttp 等现代网络库默认忽略用户证书在 macOS需在钥匙串中将证书标记为“始终信任”。注意Android 10 对用户证书限制更严部分 App尤其是银行类会启用 Certificate Pinning证书固定此时 Charles 无法解密其流量——这不是 Charles 失效而是 App 主动拒绝任何非预置证书。应对方案是反编译 App 查看 pinned cert 列表或使用 Frida 注入绕过超出本文范围但需知此为正常限制非工具缺陷。2.3 为什么它比浏览器 DevTools 更“底层”——协议栈视角的差异Chrome DevTools 的 Network 面板工作在Application Layer应用层之上它看到的是浏览器内核Blink/V8解析完 URL、组装好 HTTP 请求头、执行完 CORS 预检、处理完 Service Worker 拦截后的“最终发出的请求”。它无法看到WebView 内嵌 H5 页面发起的请求是否被原生层劫持App 自研网络库如 OkHttp 拦截器添加的自定义 HeaderDNS 解析阶段的失败如 hosts 未生效导致域名解析超时TCP 连接建立耗时、TLS 握手轮次、证书链完整度等底层指标。而 Charles 运行在Transport Layer传输层与 Application Layer 之间它看到的是原始 TCP 流经代理时的明文 HTTP 报文HTTPS 则是解密后的明文。这意味着它能精确统计每个请求的DNS 查询时间、TCP 连接时间、TLS 握手时间、首字节时间TTFB、内容下载时间并以瀑布图直观呈现它能捕获到被 Service Worker 拦截后“根本没发出去”的请求显示为红色 Failed它能识别出客户端发送的非法 HTTP/1.0 请求如缺少 Host 头而浏览器早已帮你补全它能暴露 App 是否在后台偷偷上传日志即使前端代码里完全没写相关逻辑。我曾用这一特性帮某音视频 App 团队定位一个“用户反馈耗电快”的问题Charles 显示该 App 每隔 30 秒就向log-report.example.com发送一个 2KB 的 POST 请求且 TLS 握手耗时稳定在 800ms —— 这明显是后台保活心跳而非用户主动行为。团队据此优化了日志聚合策略将 30 秒高频上报改为 5 分钟批量压缩上传实测续航提升 18%。3. 核心功能实操详解从“能用”到“用透”的 5 个关键能力3.1 SSL Proxying不只是开启而是精准控制哪些域名走解密Charles 默认不自动解密 HTTPS必须手动配置 SSL Proxying 规则。很多人习惯性勾选Enable SSL Proxying后就以为万事大吉结果发现某些请求仍是灰色encrypted或出现大量证书警告。正确做法是右键目标请求 → SSL Proxying → Enable SSL Proxying或全局配置Proxy → SSL Proxying Settings → Add。这里的关键参数有三个字段值示例说明实操建议Location*.example.com支持通配符和正则*匹配任意子域名?匹配单字符优先用*.api.example.com而非api.example.com避免漏掉v2.api.example.comPort443留空则匹配所有端口指定端口号如某些测试环境用8443生产环境基本都是443测试环境务必确认实际端口Enabled✅是否激活该规则新增规则后必须勾选否则不生效实操心得我习惯在项目初期就建一个全局规则*:*匹配所有域名所有端口然后在调试时右键关闭不需要解密的请求如 Google Analytics、微信 JS-SDK避免证书警告泛滥。这样既保证关键接口可见又不污染调试视野。3.2 Breakpoints不是断点调试代码而是“请求手术刀”Breakpoints 功能常被低估。它不像 IDE 断点那样暂停线程而是让 Charles 在请求到达代理时暂停转发给你一个编辑窗口让你像外科医生一样对请求头、请求体、响应头、响应体做任意修改再决定“放行”“丢弃”或“返回自定义内容”。启用方式Proxy → Breakpoint Settings → Add配置同 SSL Proxying。但真正威力在于使用时机复现 401 错误拦截登录后的一个业务请求删掉Authorization: Bearer xxx头点击 Execute立即看到前端弹出“登录失效”提示测试空响应体拦截一个 GET 接口在响应 Body 编辑框清空内容状态码改为200点击 Execute观察前端如何处理空数据注入调试 Header在请求头添加X-Debug-Mode: true后端据此返回额外日志字段无需改代码模拟网络延迟不修改内容但在 Breakpoint 窗口点击 “Execute” 前先切到 Timing 标签页拖动 Delay 滑块设为 3000ms再执行——完美模拟弱网。注意Breakpoint 窗口里的 “Execute” 按钮不是“发送”而是“将当前编辑后的请求转发给真实服务器”。如果只想返回本地内容应点击 “Response” 标签页勾选Return custom response填写状态码、Header、Body 后再点 Execute。3.3 Map Local本地文件即接口告别“改后端代码等部署”Map Local 是 Charles 最具生产力的功能。它允许你将任意 HTTP(S) 请求无缝映射到本地磁盘上的一个文件且自动继承原始请求的 Method、Headers、Query Params并将文件内容作为响应体返回。典型场景前端开发中后端接口尚未完成你有一个mock/user.json文件想让GET https://api.example.com/v1/user直接返回它测试需要验证某个接口返回 500 时的降级 UI你准备一个error_500.json状态码设为 500产品想快速预览新文案效果你修改本地config.json里的文案字段刷新页面即生效。配置步骤在 Structure 视图中右键目标请求 →Map Local…勾选Enable mapping点击Choose…选择本地 JSON/HTML/JS 文件可选勾选Use external editor用 VS Code 等实时编辑文件保存后 Charles 自动热更新点击 OK。关键细节Map Local 默认只匹配完全相同的 URL含 Query String。若需匹配带不同参数的请求如/user?id1和/user?id2必须勾选Match by pattern并将 Location 改为正则如https://api\.example\.com/v1/user\?.*。另外响应头中的Content-Type会自动根据文件扩展名设置.json→application/json但若需自定义如返回text/plain可在文件开头添加注释// Content-Type: text/plain。3.4 Rewrite比 Map Local 更灵活的“运行时文本替换引擎”Map Local 适合整文件替换而 Rewrite 则是“在原始响应流上做字符串替换”。它不改变响应结构只做轻量级文本修正非常适合修复前端硬编码的错误 CDN 域名如把http://old-cdn.com替成https://new-cdn.com给所有 HTML 响应注入调试脚本在/body前插入script srcdebug.js/script屏蔽第三方统计代码删除script srcanalytics.js整行统一修改 API 响应中的时间戳格式如把2023-01-01T00:00:00Z替成2023-01-01 00:00:00。配置入口Tools → Rewrite… → Add Rule。核心字段Location: 同前指定作用域Type: 选Response Body最常用或Request Header等Match: 填正则表达式如http://old-cdn\.comReplace: 填替换内容如https://new-cdn.comCase sensitive / Regex: 根据需要勾选。实操技巧Rewrite 支持多条规则链式执行。例如先执行一条规则把所有img src...的src属性值提取出来再执行第二条规则将提取的 URL 前缀替换。但要注意顺序——规则按列表从上到下执行后一条规则作用于前一条的输出结果。3.5 Repeat Advanced不只是重发而是“压力测试雏形”Charles 的 Repeat 功能大家都会用右键请求 →Repeat。但Repeat Advanced右键 →Repeat Advanced…才是隐藏高手。它允许你修改请求 Method如把 GET 改成 POST批量修改请求头如添加X-Test-Env: staging设置并发数Concurrent requests模拟多用户同时请求设置重复次数Iterations和间隔Delay between iterations查看每次请求的独立响应时间、状态码、响应大小。这已具备简易压测能力。例如测试一个搜索接口的稳定性并发数设为 10重复 50 次间隔 100ms查看瀑布图中所有请求的 TTFB 分布若出现大量 2s 的毛刺说明后端存在锁竞争导出结果为 CSV用 Excel 统计 95% 响应时间P95作为性能基线。注意Repeat Advanced 不会自动处理 Cookie 或 Token 过期。若请求依赖登录态需提前在 Request Headers 中手动添加有效的Cookie或Authorization或配合Sequence功能Tools → Sequence…先执行登录请求获取 Token再执行业务请求。4. 真实排障记录那些官方文档不会写的 7 个致命坑4.1 “手机连不上 Charles”——90% 是这 3 个原因现象根本原因排查步骤解决方案手机显示“无法连接到服务器”Charles 未开启远程连接或防火墙拦截1. 查 Charles Proxy Settings → Access Control Settings 是否勾选Allow remote connections2. 在 Mac 终端执行lsof -i :8888确认进程监听*:8888非127.0.0.1:88883. 执行sudo ufw statusUbuntu或检查 macOS 防火墙设置在 Access Control Settings 勾选后重启 CharlesmacOS 防火墙需在“系统偏好设置 → 安全性与隐私 → 防火墙 → 防火墙选项”中允许 Charles手机能连上但看不到任何请求手机 DNS 未指向 Charles 所在电脑1. 手机浏览器访问http://chls.pro/ssl若无法下载证书说明网络不通2. 在手机终端需 root或电脑 ping 手机 IP确保手机和电脑在同一局域网关闭手机“智能切换网络”“移动数据自动切换”等干扰项Wi-Fi 设置中手动填 DNS 为电脑 IP能看到请求但全是灰色encryptedSSL Proxying 未针对该域名启用1. 在 Structure 视图中找到灰色请求右键 →SSL Proxying → Enable SSL Proxying2. 检查 Proxy → SSL Proxying Settings 中是否有匹配规则养成习惯首次抓到新域名请求立即右键启用 SSL Proxying全局规则*:*可作兜底4.2 “安装了证书还是提示不安全”——Android/iOS 证书信任深度指南iOS 15苹果收紧了证书管理。安装.pem证书后必须进入设置 → 已下载描述文件 → 安装 → 通用 → 关于本机 → 证书信任设置 → 找到 “Charles Proxy CA” 并开启开关。仅安装不开启等于没装。Android 7~9用户证书可被 OkHttp 等库识别但需在 App 的network_security_config.xml中显式声明certificates srcsystem /和certificates srcuser /。Android 10默认忽略用户证书。解决方案有两个将 Charles Root Certificate 导出为.crt格式用 ADB 推送到/system/etc/security/cacerts/需 root在 App 的network_security_config.xml中添加debug-overrides块仅在 debug buildType 下信任用户证书推荐无需 rootdebug-overrides certificates srcuser / /debug-overrides4.3 “Map Local 不生效”——5 个检查清单✅ 是否勾选了Enable mapping新手最高频遗漏✅ 本地文件路径是否包含中文或空格Charles 对路径编码敏感建议全英文路径✅ 文件是否被其他程序占用如 VS Code 正在编辑尝试关闭编辑器再试✅ 是否勾选了Match by pattern若未勾选URL 必须完全一致含 query string✅ Charles 是否开启了Throttling限速限速模式下 Map Local 可能延迟生效临时关闭即可。4.4 “Rewrite 替换失败”——正则表达式避坑不要用.*匹配跨行内容HTTP 响应体中换行符是\r\n.*默认不匹配换行。应使用[\s\S]*或启用Dot matches newline选项特殊字符需转义.$^*?()[]{}|\在正则中均有特殊含义如匹配字面量.必须写\.贪婪 vs 懒惰script.*/script会匹配从第一个script到最后一个/script的全部内容贪婪应写script.*?/script懒惰。4.5 “Repeat Advanced 并发请求全失败”——状态保持缺失Repeat Advanced 默认不维护会话状态。若原请求依赖 Cookie 或 Token重发时这些 Header 不会自动携带。解决方案在 Repeat Advanced 窗口中手动在Request Headers标签页添加所需 Header或使用Sequence功能先录制一个登录流程含获取 Cookie再录制业务请求Sequence 会自动将前一步的响应 Cookie 注入下一步请求。4.6 “Charles 卡死/崩溃”——内存与日志的平衡术Charles 默认保存所有请求的完整 Body包括图片、视频长时间运行极易吃光内存。解决方案Proxy → Recording Settings → uncheckRecord request/response bodies只记录 HeadersView → Layout → Configure Layout取消勾选Show request/response bodies界面更轻量Edit → Clear Charles’ History定期清理快捷键 ⌘K启动时加 JVM 参数在 Charles 安装目录的Contents/Info.plist中修改VMOptions增加-Xmx2g限制最大堆内存为 2GB。4.7 “找不到某个请求”——Charles 的“隐身模式”揭秘Charles 默认不显示以下请求OPTIONS 预检请求CORS 预检由浏览器自动发起Charles 默认过滤需在Proxy → Recording Settings → Include OPTIONS requests勾选WebSocket 流量需单独开启Proxy → WebSocket Tracking → Enable WebSocket trackinglocalhost 请求Charles 默认不代理127.0.0.1和localhost需在Proxy → Recording Settings → Include localhost勾选HTTP/2 流量Charles 10.5 支持 HTTP/2 解密但需在Proxy → SSL Proxying Settings → Enable HTTP/2 support勾选且服务端必须支持 ALPN。5. 进阶实战用 Charles 构建你的个人调试工作流5.1 “一键复现线上 Bug”工作流很多线上 Bug 无法本地复现因为依赖特定用户数据、特定设备指纹、特定网络环境。Charles 可将其固化为可共享的调试资产录制线上会话在用户复现 Bug 时用 Charles 录制完整请求链开启Record request/response bodies标注关键请求在 Structure 视图中右键关键请求 →Annotate…写明“此处返回 500 导致页面白屏”导出为 .chls 文件File → Save Charles Session生成可双击打开的会话文件分享给后端将.chls文件发给后端同学他双击打开即可看到完整上下文包括请求头、请求体、响应体、响应时间无需你口头描述“我点了按钮然后…”本地复现用Sequence功能将录制的请求按顺序重放或用Breakpoint在关键步骤暂停手动修改参数触发相同路径。我所在团队已将此流程标准化所有线上 Bug 提 Jira 时必须附带.chls录制文件。后端平均定位时间从 2 小时缩短至 15 分钟。5.2 “第三方 SDK 行为审计”清单App 集成的 SDK如推送、统计、广告常在后台静默通信影响性能与隐私。用 Charles 快速审计审计项操作步骤预期结果异常信号域名归属右键 SDK 相关请求 →Copy → cURL Command粘贴到终端执行curl -v https://sdk-domain.com查看* Server certificate:证书 Subject 应与 SDK 官方域名一致Subject 为未知公司或 CN 为*.google.com但请求发往ad-tracker.net上报频率选中所有该 SDK 的请求 → 右键 →Export → Export Selected Sessions为 CSV用 Excel 统计每分钟请求数合理频率推送 SDK 每 5~10 分钟心跳一次统计 SDK 每次页面浏览上报 1 次每秒上报 10 次或后台持续上报无用户交互时数据内容查看响应体或请求体搜索idfa、android_id、imei、location等敏感字段合规 SDK 应对敏感字段脱敏如idfa: XXXX-XXXX-XXXX-XXXX明文传输完整 IDFA或上报 GPS 坐标精度达小数点后 6 位5.3 “自动化调试脚本”初探Charles Python 无限可能Charles 提供Charles Proxy API基于 HTTP可通过 Python 脚本远程控制。例如import requests # 清空历史记录 requests.get(http://localhost:8888/quit) # 开启/关闭 SSL Proxying需先在 Charles 中配置好规则 requests.get(http://localhost:8888/sslproxying/enable) requests.get(http://localhost:8888/sslproxying/disable) # 获取最近 10 个请求的摘要 resp requests.get(http://localhost:8888/commands/last-requests?count10) print(resp.json())结合此 API你可以写一个 Jenkins 构建后自动抓取测试环境 API 调用的脚本开发一个内部工具输入 URL自动启动 Charles、配置代理、抓取、分析响应头是否含Cache-Control与 Selenium 集成在 Web 自动化测试中实时监控 XHR 请求成功率。提示API 默认只监听127.0.0.1如需远程调用需在 Charles 启动时加参数-proxy-api 8888端口可自定义并在 Proxy Settings 中开启Enable proxy API。6. 总结Charles 不是终点而是你调试认知的起点我用 Charles 超过八年从最初只会点“Save”导出 HAR到现在能用 Rewrite 写正则批量修复 CDN用 Sequence 模拟复杂业务链路用 API 对接 CI/CD。它的价值从来不在功能列表有多长而在于它强迫你直面 HTTP 协议本身当你亲手删掉一个Cookie头看到登录态丢失当你把Content-Type从application/json改成text/html看到页面乱码当你在 Breakpoint 里把200改成503看到前端优雅降级——这些瞬间HTTP 不再是抽象概念而是可触摸、可修改、可预测的实体。所以别再说“Charles 过时了”。过时的不是工具而是我们停留在“能看”的浅层用法。真正的调试高手早把 Charles 当成呼吸般自然的延伸它不声不响却在每一次接口异常、每一次性能抖动、每一次线上谜题中为你点亮那束最精准的光。下次当你面对一个“说不清道不明”的网络问题时别急着翻文档、查 Stack Overflow先打开 Charles右键那个可疑的请求点下 “Breakpoint”——答案往往就在你按下 “Execute” 之前那几秒钟的凝视里。