排查接口问题的时候,我习惯性先打开Charles,而不是上来就去翻后端日志。原因很简单:抓包能让你看到双方“真正经过网络传输”的数据,而不是你代码里以为的数据。Charles这个抓包调试工具我用了不少年,前后端联调、移动端调试、线上故障定位,它基本都能覆盖。这篇文章我会把这些年实际用下来的经验整理成一整套从入门到精通的清单,包括证书安装、断点改包、弱网模拟、移动端抓包,以及一次完整的问题复盘。适合刚接触抓包的初学者,也适合想进一步用好工具的熟练开发者。
1. 为什么前后端都在用Charles:一次接口联调的真实起点
1.1 浏览器DevTools和Charles,差在哪
如果你平时只调试自己负责的页面,Chrome或者Firefox自带的DevTools其实够用。但真正经历过几轮联调之后你会发现,浏览器开发者工具有很多解决不了的场景:用户手机上报来的线上故障,你本地根本复现不了;你明明在代码里写了某个请求参数,真正发出去的请求里却根本没有;后端甩过来一句“我这边返回是对的”,你得拿出证据去对齐。
这些场景里,Charles作为一个中间代理,让你能独立观察所有进出流量,不受框架、缓存和代码逻辑的影响。多说一句,DevTools和Charles看的数据维度并不相同。DevTools看的是浏览器内部的网络事件,而Charles站在操作系统代理层面,看到的是TCP层之上真正经过网卡进出的原始HTTP流量。后者不仅包括浏览器,还包括小程序、原生App、桌面客户端乃至路由器上其他设备的请求。所以只要涉及到真实设备的调试,Charles几乎是绕不开的。
1.2 它到底解决了哪几类痛点问题
按我自己的使用频率,Charles主要解决这几类问题。第一,老项目没有接口文档,想搞清楚请求长什么样,直接抓包看线上请求体和响应体,比翻文档快得多。第二,前后端对参数理解不一致,你说你传了,他说他没收到,包一放出来,问题出在哪个环节一目了然。第三,移动端完全是个黑盒,App里没有开发者工具,只有Charles能让你看到HTTPS里面的真实内容。第四,复现疑难问题,通过断点修改请求或响应,人为制造各种边界情况。第五,后端接口还没写好,前端用Map Local模拟一份返回继续开发。
举个实际例子,之前某个活动页的接口一直报错,后端始终坚持自己返回了正确的字段。我把抓包结果导出来一看,网关层在中间把某个字段名改写成了另一个名字,前端按约定字段去解析,自然拿不到值。这种问题不看原始包,光靠两边口头对线,能吵一下午。
2. 安装与证书信任:最容易劝退新手的第一道门槛
2.1 安装、JDK依赖和首次启动
Charles是用Java写的,所以电脑上得先有Java运行环境。Windows环境装个JDK 8或者JDK 11基本不会出兼容性问题,macOS也差不多;Linux下如果用包管理器安装,注意OpenJDK版本不要太新,某些新版本会和Charles 4自带的Java 8依赖打架。下载安装包一路下一步就行,它本身是收费软件,但试用版足够你跑通这里讲到的所有功能,只是每次启动后要等几秒。
启动之后你会看到默认代理端口是8888,这个端口要记牢。首次使用建议先去Proxy > Proxying Settings里确认HTTP代理勾选了,端口是8888。后面所有客户端、手机、模拟器做代理,全都指向这个端口。我自己习惯把端口改成更容易记的四位数,比如9999,改完之后注意同步更新所有设备上的代理配置,这个细节后面会反复碰到。
2.2 SSL证书安装与“完全信任”的那些坑
只配代理还远远不够。不装证书时,抓HTTPS内容全是乱码,因为Charles只能看到密文。要解密TLS流量,必须让客户端信任Charles自己生成的根证书。
Windows上操作路径是Help > SSL Proxying > Install Charles Root Certificate,装完之后到证书管理里确认它出现在“受信任的根证书颁发机构”这个目录下。macOS上路径差不多,但装完之后打开钥匙串,手动把证书的信任级别改成“始终信任”,否则后续连接经常出问题。
容易被忽略的一步是:Proxy > SSL Proxying Settings 里要勾选Enable SSL Proxying,并添加SSL代理规则。不想细粒度配置的话,直接填*:443,表示对任意域名443端口的流量都做SSL解密。这里有个经验之谈:每次改动SSL Proxying设置后,建议重启一次Charles,改完不重启,规则经常不会立即生效,这是我踩过好几次的坑。
还有一个坑来自浏览器本身。不少现代浏览器默认不走系统代理,给浏览器装了代理扩展,或者手动在局域网设置里填好代理地址,流量才会经过Charles。遇到“明明配了代理但就是抓不到HTTPS包”的情况,先检查证书是否被浏览器信任,再检查流量是否真的走到了代理端口。
2.3 常见启动失败与代理端口冲突
启动失败大多是两类原因:Java版本不兼容,或者端口被占用。如果8888起不来,换一个高位端口比如9999,然后同步更新所有客户端的代理配置。Windows防火墙拦截也会导致手机连不上代理,第一次用手机抓包前,记得在防火墙规则里放行Charles对应的端口。
还有一个比较迷惑的场景:电脑上同时运行着其他代理类工具,它们可能会抢占系统代理开关。Charles明明开着,浏览器流量却从别的代理走,界面上一片安静,看着就像故障。排查方式很简单,把系统代理关掉再打开,然后看Charles里是否有新连接进来。没有的话,再逐个关闭其他代理类工具做排除。
3. 日常抓包的几个小习惯:过滤、聚焦、保存、导出
3.1 过滤无关流量,只看真正关心的域名
不设置过滤的话,Charles的列表会被广告、统计、CDN请求刷屏,想在几百条请求里找到目标接口,非常难受。左下角有个Filter输入框,输入关键字就能按URL过滤,支持正则。
更稳的方式是:右键目标域名,选择Focus,让这个主机进入上方的Focused Host列表。之后整个Charles界面只显示这个域名及其子域名的流量,其他请求自动折叠不见。我自己的习惯是抓包一开始就把焦点设置好,尤其是联调阶段,只看业务域名,不关心第三方资源。Focused Hosts可以在Proxy > Recording Settings里维护,支持通配符,比如*.example.com这种写法。抓包结束后再清掉焦点,恢复完整列表,避免下次忘了看其他流量。
3.2 请求响应查看技巧和乱码处理
抓到包后双击条目,能看到Request、Response、Timeline几个面板。Response里如果显示乱码,先看Content-Encoding,返回gzip时Charles通常会自动解压,但有些场景(比如WebSocket)不会自动处理,或者响应体被Base64编码甚至自定义加密,这时候只能另想办法。JSON格式的接口返回,切到JSON视图就能格式化查看,比看原始文本舒服得多。
这里有个小技巧:右键请求 -> Copy cURL Request,可以直接复制出一条完整的curl命令。把这条命令发给后端同事,或者丢到服务器上复现请求,特别方便。尤其适合“本地通、服务器不通”这类环境差异问题,在服务器上跑一遍curl,结果一目了然。如果返回内容是图片、视频等二进制资源,不要尝试用JSON视图解析,会直接报错,切回Hex视图看十六进制反而更直观。
3.3 会话保存与导出,让每次排查都有迹可循
排查线上问题时,最怕的就是“当时在现场,事后无法复现”。Charles的会话保存功能很有价值:File > Save Session,保存出一个.chls文件,之后随时打开,所有请求、响应、时间线都还在,跟现场一模一样。我处理疑难杂症时,都会先把现场会话保存一份,再做各种实验。
导出功能也很实用。单个请求可以右键选择Export,导出成JSON或CSV格式,方便写排查报告或者喂给脚本做自动化统计。联调结束后,把接口返回导出成JSON,还可以顺便当作mock数据文件。这些操作看起来不起眼,但真正遇到需要隔天继续排查的问题时,一套完整的会话文件比什么都可靠。
4. 从“看包”到“改包”:断点、映射、重写与重放
4.1 Breakpoint断点:改请求、改响应全流程
看包只是基本功,Charles真正的价值在于“改包”。右键目标请求,勾选Breakpoints,这个请求再次发生时会被Charles拦截下来,弹出编辑窗口。你可以修改Request里的URL、Header、Body再点Execute,也可以直接修改Response后返回给客户端。
断点很适合做边界值测试。比如后端返回status=500时前端怎么表现,不需要真的等后端出故障,直接用断点把响应改成500就行。又比如某个字段为空时页面会不会崩溃,把返回里的字段删掉再Execute,立刻能看到前端表现。需要注意的是,断点是一次性的,只对勾选后的那次请求触发,不适合高频接口逐个点。如果要按域名配置全局断点,可以在Proxy > Breakpoint Settings里设置预设规则,匹配URL和请求类型。另外实测断点开关偶尔不生效,设置完重新勾选一下请求,或者重启Charles就好了。
4.2 Map Local / Map Remote:用一个本地文件“冒充”后端
Map Local是前后端联调阶段的效率神器。右键某个接口 -> Save Response,保存一份真实的JSON响应到本地;再右键该接口 -> Map Local,指定刚才保存的本地文件。之后不用改任何前端代码,这个接口的响应就完全变成你本地文件的内容。
你可以在本地文件里任意改数据,用来模拟各种状态:把列表改成空数组、把状态码改成异常、把一个字段的值改得非常大。前端针对各种响应状态的页面逻辑,都可以这样提前验证。有些场景下,线上接口数据量太大,本地mock一个精简版本还能加快页面打开速度。
Map Remote则是把请求重定向到另一台服务器地址,适合做环境对比。同一个请求,分别打到测试环境和灰度环境,看两边返回差异。这里要注意匹配规则,正式使用Map Local Settings时建议为每个接口配置明确的pattern,支持通配符和正则。同时注意勾不勾选“保留Host Head”这个选项,对Cookie、跳转、鉴权类接口影响很大,很多人第一次用Map Local时接口通了但Cookie失效,就是这里没配对。
4.3 Rewrite和Repeat:批量修改与一键重放
断点适合单次观察,如果需要批量修改规则,用Rewrite。比如你想把线上所有接口响应的某个Header值统一替换成新值来做验证,不用一个包一个包去断点,直接在Tools > Rewrite里配置规则,匹配到指定URL后自动改写指定的Header、Status、Body内容,然后点击Enable启用。这种全局规则特别适合A/B对比:同一套界面,换个Header就切换到不同的功能开关,省去反复配置环境的时间。
Repeat是重放请求:选中一个请求,右键Repeat,Charles会按照同样的Request再发一遍。它的价值在于验证“是不是偶发”。某个接口第一次调用失败了,立刻Repeat几次,如果偶发概率高,很快就能复现。Repeat Advanced还可以设置重复次数和并发数,用来模拟连续点击或快速刷新的情况,定位竞态问题很方便。
4.4 Throttle限速:模拟弱网环境
弱网测试最省事的方式就是打开Proxy > Throttle Settings,设置带宽、延迟和丢包率。比如我想模拟3G网络,一般设置带宽300kbps、延迟500ms左右,体感已经很接近了。直播页面卡顿优化、图片懒加载的判断时机、请求超时重试逻辑,都可以靠Throttle提前发现问题。
实测下来有个经验:如果挂的是真实手机,Throttle的效果比模拟器稳定。安卓模拟器或iOS模拟器里,网络层限制有时不准,低延迟情况下尤其明显,得到的结论可能和真机不一致。所以比较关键的弱网验证,我建议用真机连接Charles来做,预算有限的情况下,用旧手机当测试机也行。
| 功能 | 用途 | 适用场景 |
|---|---|---|
| Breakpoints | 单次拦截并修改请求/响应 | 边界值测试、临时数据篡改 |
| Map Local | 将接口响应替换成本地文件 | 后端未就绪、mock数据、异常模拟 |
| Map Remote | 将请求重定向到其他环境 | 环境对比、灰度切换 |
| Rewrite | 批量改写Header/Body/状态码 | 全局规则、A/B验证、开关切换 |
| Repeat | 重放相同请求 | 偶发问题复现、并发模拟 |
| Throttle | 限制带宽/延迟/丢包率 | 弱网测试、卡顿优化 |
5. 移动端抓包实战:证书、真机和Android 7.0以上那些坑
5.1 先让手机流量走Charles
手机和电脑连同一个Wi-Fi,在手机的Wi-Fi设置里配置HTTP代理,填电脑的局域网IP和Charles的端口。这里注意一定用IPv4地址,不要写localhost,手机端没有本地Charles。设好之后,在手机上打开任意网页,电脑上Charles会弹出“是否允许来自远程设备的连接”的确认框,这个弹窗必须点Allow,否则手机会一直建立不了连接。
连接成功之后,大部分HTTP请求都能看到了。如果想抓HTTPS内容,仍然需要做证书安装。这里还有一个容易忽略的点:手机代理配置好之后,上不了网了,先检查Charles是否还在运行,以及电脑防火墙是否放行。很多第一次配手机抓包的人,卡在这一步就放弃了,其实就两个检查项:弹窗Allow有没有点,防火墙端口放没放行。
5.2 iOS安装证书后为什么还是抓不到HTTPS
iPhone在Safari里访问chls.pro/ssl下载证书,安装完成之后并不代表完事,这只是证书进了描述文件列表。iOS 10.3之后多了一道开关:设置 > 通用 > 关于本机 > 证书信任设置,必须把Charles对应的根证书打开“完全信任”。如果没打开这个开关,即使证书显示已安装,真实请求依然报SSLHandshake错误,客户端直接拒绝连接。
还有一部分App内置了证书固定(SSL Pinning),安装Charles证书对它们也不起作用。这种App通常走非标准证书链或者额外校验指纹,表现就是普通浏览器能抓,App里一片失败。处理办法一般是从普通HTTP流量和崩溃日志入手,如果确实需要调试这类App,得考虑更适合的方案,这就不是Charles本身的范畴了。
5.3 Android 7.0以后的用户证书信任制度
Android 7.0之后,以targetSdkVersion 24或更高版本构建的App,默认不信任“用户证书”,只信任系统证书。也就是说,哪怕你在安卓手机上正常安装了Charles证书,很多App的HTTPS流量依然可能失败,控制台里经常出现trust anchor for certification path not found之类的错误。
解决办法常见的有三个。第一,临时用Android 6.0的模拟器做调试,省事,适合偶尔抓一次包。第二,在测试机上获取root权限后,把Charles证书从用户证书区复制到系统证书区,证书文件名必须按系统规则生成,不同系统的命令大同小异,这个方案适合你有一台专门用来做调试的测试机。第三,如果是自己家的App,在AndroidManifest里声明networkSecurityConfig,调试版本允许信任用户证书,这是最正规的做法。注意发布版本不要保留这种配置,否则等于把用户的证书信任面扩大了,有安全风险。
5.4 常见报错快速对照表
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
| 手机连不上Charles | 防火墙拦截、弹窗未点击Allow、端口被占用 | 放行端口,重新连接,换高位端口 |
| 列表里全是CONNECT,没有内容 | SSL Proxying规则未启用 | 在SSL Proxying Settings里添加*:443并重启 |
| 证书已装但一直SSLHandshake | iOS未打开完全信任,或App做了SSL Pinning | 打开证书信任开关,换调试方案 |
| Android上App直接不请求 | Android 7.0+不信任用户证书 | 换低版本模拟器,或用系统证书方案 |
| 浏览器能抓但App抓不到 | App不使用系统代理 | 检查App是否有自己的联网配置 |
6. 一次白屏问题复盘:把Charles的完整能力串起来
6.1 复现现场:白屏和日志里的302
某个活动页面在部分用户手机上白屏,用户提供的前端日志只有一句“request failed”。为了拿到第一手证据,我用Charles把手机代理指过来,清空会话后让用户重新打开页面。抓包发现请求链很有意思:页面接口先收到302跳转到登录页面,登录页又带着回调地址302跳回来,形成一个循环。前端最终拿到的是空响应,所以页面一直是白屏。
这个现象如果只看代码很难发现,因为代码逻辑本身是“未登录时跳转登录页”,看起来完全正常。Charles的价值在这里体现得最明显:不依赖任何代码日志,直接看到每一次请求和响应的状态码。我先把这次会话存成了 .chls 文件,防止后续操作把现场覆盖掉。
6.2 改包验证:锁定前端还是后端
接下来用断点把302改成200,并附带一份模拟的页面数据,页面立刻正常渲染,基本可以判断前端本身没有问题,问题出在跳转条件。然后换用Map Local,把用户信息接口的返回替换成“已登录”状态的数据,页面依然可以正常展示。到这里就明确了:前端逻辑和后端页面接口都没大毛病,是登录态判定环节出了问题。
再对比正常用户和异常用户的抓包内容,差异很快就出来了。异常用户发出的请求里,带上来的Cookie字段缺少了某一段,网关识别不了用户身份,于是强制302循环。后端在网关层面把该字段加入白名单之后,异常用户重新访问,问题不再出现。
6.3 整个定位过程的核心思路
回看整个排查过程,我其实没有翻后端日志,全靠Charles的过滤、改包、对比三个能力,把范围一步步缩小到“登录态Cookie缺失”。如果一上来就查日志,会被各种正常请求噪声干扰,反而拖长时间。这也是我把Charles列为接口问题排查第一工具的原因:它提供了一份可交互、可改写的迷你版线上环境,让你能像做实验一样去验证假设。
排查接口类问题,我的通用顺序是:先抓包看实际请求和响应,再做最小改动验证假设,最后拉出正常和异常的对比包。这套流程用Charles来做,每一步都有对应的功能支撑,定位速度比其他方式快不少。
最后分享一个我自己的习惯:每次开始抓包前,先清空会话,设置好Focused Host,确认SSL Proxying规则已开,再让客户端发起请求。提前一分钟做这三件事,能省下大量在几百条请求里翻找的时间。如果你刚开始接触Charles,别被证书和代理这些概念吓到,把它们当成一道固定的“过安检”流程,走通之后你会发现,大部分两边扯皮的问题,其实几分钟就能定位。