最近排查一个某内部系统的登录超时问题,后端日志显示请求已经到了,但响应时间一直波动,浏览器 F12 只能看到 Web 端的流量,App 里真正传参的情况完全看不见。折腾到半夜,最后是靠 Mitmweb 把完整的请求响应链捞出来,才定位到是某个响应头被上游网关吃掉,导致客户端一直重试。这件事让我意识到,Mitmweb 的参数配置才是真正决定调试效率的东西。不少同学是直接敲mitmweb就开抓,等遇到端口占用、证书不生效、脚本注入失败、流量文件打不开这些情况,才开始一页页翻文档。这篇文章就围绕 Mitmweb 参数配置来写,把我自己实际用下来最有价值的参数组合、常见坑和排查思路一次讲清楚。适合前后端开发、测试同学,以及做接口联调或流量分析的同学参考,不管你是第一次接触还是已经用过一段时间,应该都能从中找到能直接抄走的命令行。1. 先把运行环境收拾利索:安装版本与证书信任这两道坎1.1 安装方式与 Python 版本Mitmweb 是 mitmproxy 工具族里的 Web 界面版本,安装走 Python 包管理最方便。我推荐在虚拟环境里安装,避免污染系统里其他 Python 项目的依赖:python3 -m venv .venv source .venv/bin/activate pip install mitmproxy安装完用版本号验证一下:mitmweb --version如果能看到类似Mitmproxy: 10.x.x的输出,说明装好了。这里的版本号很重要,后面不少参数的行为在不同大版本之间会有差异。我自己就遇到过照着老教程写--set websocket=true,结果新版直接不识别的情况。提示:不建议用sudo pip install全局安装,很容易把系统 Python 环境搞乱。虚拟环境是成本最低的隔离方案。1.2 证书信任:为什么 HTTPS 流量全是乱码第一次启动 Mitmweb 后抓取 HTTPS 请求,会发现流量列表里都是解密失败或连接错误,这不是工具坏了,而是它作为中间层需要被客户端信任。Mitmweb 首次运行会在用户目录生成一套本地 CA 证书,路径大概是~/.mitmproxy/mitmproxy-ca.pem。要把这套证书装到测试设备上,最省事的办法是直接用 Web 控制台自带的证书下载页。电脑本机调试:访问http://127.0.0.1:8081/cert/pem下载证书,双击导入系统信任区。手机真机调试:手机连上同一局域网后,访问http://<电脑IP>:8081/cert/,按设备类型下载安装。Android 7 及以上版本,很多应用默认不信任用户 CA 证书,即使你安装了也仍然抓不到,需要在应用的networkSecurityConfig里显式允许用户 CA,或者用调试包。iOS 设备安装描述文件后,还要去“关于本机 > 证书信任设置”里打开完全信任开关,否则 Safari 会报证书错误。这一步经常被忽略,导致很多人误以为工具不支持 HTTPS。实际上只要证书信任了,剩下的参数配置才有意义。1.3 用一个最小命令验证环境证书弄完,先别急着上复杂参数,跑一个最小命令确认环境正常:mitmweb --listen-port 8899 --web-port 8081启动后浏览器打开http://127.0.0.1:8081,能看到消息列表和漏斗过滤栏。这时候随便访问一个 HTTP 页面,消息列表里应该出现对应记录。能走到这一步,说明安装、端口、Web 控制台三个环节都通了,再往下加参数就不会互相甩锅。2. 上手第一个参数组合:流量入口和控制台端口是两个独立维度2.1 端口参数各自管什么Mitmweb 启动后虚线监听的是两个端口,很多人把它们混为一谈,导致怎么调都不对。这里我建议先记住:--listen-port管的是流量入口,客户端要把流量转发到这个端口;--web-port管的是调试界面,浏览器通过这个端口看抓包结果。参数默认值作用--listen-host127.0.0.1流量入口绑定的地址--listen-port8080客户端流量转发过来的端口--web-host127.0.0.1Web 控制台绑定的地址--web-port8081浏览器访问控制台的端口默认情况下,mitmweb会把流量入口和控制台都绑在127.0.0.1,也就是只允许本机访问。这个设计是安全的,但坑也在这:手机真机调试时,如果你只改端口不改成0.0.0.0,手机根本连不上你的电脑。2.2 最常见的本地真机抓包组合我自己最常用的基础组合是:mitmweb --listen-host 0.0.0.0 --listen-port 8899 --web-port 8081拆开来看,--listen-host 0.0.0.0表示流量入口监听所有网卡,这样同一局域网内的手机才能把流量指过来。--listen-port 8899是手机端要填的端口,我不用默认 8080 是因为开发机上这个端口被各种服务占用的概率太高。--web-port 8081保持默认,方便记忆。手机端在 Wi-Fi 设置里把“HTTP 转发”或“手动配置”指向电脑的局域网 IP 和 8899 端口,然后浏览器打开http://<电脑IP>:8081就能看到手机流量实时出现。注意这时候 Web 控制台绑定了127.0.0.1,所以只有电脑本机能访问,手机访问不到也没关系,这反而是更安全的配置。2.3 端口占用与防火墙排查如果启动时提示Address already in use,说明端口已经被别的进程占用了。排查命令:lsof -i :8899 netstat -tlnp | grep 8899找到占用进程后,要么换端口,要么先处理掉旧进程。还有一个容易被忽略的点是系统防火墙:本机抓包通常不受影响,但手机真机抓包时,如果电脑开启了防火墙,即使监听地址是0.0.0.0,外部连接也可能被拦掉。需要放行对应端口,或者把电脑加入同一个无隔离网络。注意:公司办公网络里可能还有更严格的网络准入策略,真机抓包前先确认这类限制,否则定位半天也连不上。3. 从“只开眼”到“能干活”:脚本注入、运行期选项与流量落盘3.1--scripts参数:给请求和响应打补丁抓包只能看数据,改数据要靠脚本注入。Mitmweb 支持用 Python 脚本拦截和修改请求响应,这在调试接口异常时特别有用。举个我实际写过的例子:# addon.py from mitmproxy import http def request(flow: http.HTTPFlow) -> None: if "login" in flow.request.pretty_url: flow.request.headers["X-Debug-Mode"] = "1" def response(flow: http.HTTPFlow) -> None: if "login" in flow.request.pretty_url: flow.response.headers["X-Backend-Latency"] = str( flow.response.timestamp_end - flow.response.timestamp_start ) print(flow.request.pretty_url, flow.response.status_code)启动时带上:mitmweb --listen-port 8899 --web-port 8081 --scripts addon.py这段脚本做的事情是:请求到达时给登录接口加一个调试头,响应返回时把后端处理耗时写进自定义响应头。配合 Web 控制台直接能看到,不用再翻后端日志去算耗时。脚本里的事件函数名是约定好的,request和response分别对应每个请求和响应的回调,比在控制台里一条条改更高效。3.2--set参数:运行时选项才是进阶的关键--set参数负责修改工具运行时的内部配置,相当于把 Web 界面“选项”页里的开关直接写成命令行。我常用的几个选项如下:选项作用适用场景block_global=false允许转发到外部地址的请求浏览器访问公网接口时,默认会被拦截stream_large_bodies=1m超过 1MB 的响应体按流式处理,不整体缓存在内存里抓取大文件下载、视频流接口ssl_insecure=true忽略上游证书校验错误本地测试自签名证书的后端服务connection_strategy=lazy延迟建立到目标服务器的连接流量很大时减少无谓的连接开销http2=false关闭 HTTP/2 支持重放请求时部分服务端对 HTTP/2 兼容不佳举个例子,默认情况下浏览器访问外部 HTTPS 网站时,Mitmweb 会因为安全策略拦截请求,很多新手第一次抓包发现列表里什么都没有,就是这个问题。排查时可以记住:消息列表里出现400或502,同时日志提示block_global,就直接在启动命令里加--set block_global=false。3.3 把流量保存下来:-w/--save-stream-file调试接口最怕“刚才明明抓到了,但忘了留证据”。-w参数可以把在线流量实时写入一个文件:mitmweb --listen-port 8899 --web-port 8081 -w trace_$(date +%Y%m%d).mitm这样生成的.mitm文件包含请求、响应、证书信息、时间戳等完整上下文,比浏览器导出的 HAR 信息更全。我通常在开始抓包前就把-w带上,哪怕用不上,事后也能回溯。3.4 离线查看流量:--read-file如果已经有一份.mitm文件,不需要重新抓包就能用 Web 控制台分析:mitmweb --read-file trace_20250101.mitm --web-port 8082这个模式非常有用,别人发你一份流量文件,你直接起一个离线 Web 控制台慢慢看,不会干扰正在运行的服务。因为不会启动新的流量入口,所以只需要指定 Web 端口即可。对比一下:在线模式是“边抓边看”,离线模式是“抓完再看”,后者遇到复杂问题可以慢慢分析,不用逼着用户一直开着电脑。4. 几个高频工作流的参数组合:真机抓包、后端联调、离线分析4.1 真机 App 抓包的标准起手式真机抓包需要把流量入口暴露到局域网,同时又要考虑外部地址访问和文件保存,我的标准命令是这样:mitmweb \ --listen-host 0.0.0.0 \ --listen-port 8899 \ --web-port 8081 \ --set block_global=false \ --set stream_large_bodies=1m \ -w trace_$(date +%Y%m%d_%H%M).mitm四个关键点:第一,--listen-host 0.0.0.0让手机能连入;第二,block_global=false避免访问公网接口时被拦截;第三,stream_large_bodies=1m避免图片或视频接口把内存撑爆;第四,-w实时落盘。这套组合覆盖了我 90% 的移动端排查场景,跑起来之后手机正常操作十分钟,基本想要的数据都能抓到。有一个容易忽略的细节:手机屏幕熄灭后,Wi-Fi 可能进入省电休眠,导致流量中断。抓包时建议在手机开发者选项里开启“不锁定屏幕”,或者至少保持屏幕常亮。4.2 后端联调时如何只关注特定接口抓包数据一大就难定位,尤其后端联调时前端页面会同时产生几十个静态资源请求。Mitmweb 的 Web 控制台左上角有过滤栏,支持~u、~q、~s等过滤表达式。比如只留下 URL 中包含api的请求:~u /api/只留下响应状态码是 500 的请求:~s 500把两个条件组合在一起:~u /api/ & ~s 500过滤只是控制台显示层面的事,不会影响抓包内容。如果确实想彻底忽略某些域名,可以在启动时用--ignore-hosts参数,例如:mitmweb --ignore-hosts 'my-internal.test'被忽略的地址不会出现在列表里,能大幅减少噪音。我用下来感觉过滤栏更灵活,因为不需要重启就能改条件,适合调试过程中不断调整关注范围。4.3 一次真实的超时问题定位过程这里用一个虚构但很典型的例子说明参数组合怎么发挥作用。某天同事反馈一个登录接口偶发超时,我按上面真机命令起好环境,手机把流量指到 8899,十几分钟后就抓到了失败请求。看响应耗时发现接近 10 秒,正常应该 200 毫秒。进一步用脚本注入把X-Backend-Latency写进响应头,很快就确认耗时发生在后端应用层,而不是网络链路。继续看请求头发现客户端带了一个过大的Cookie,后端在处理时触发了某个校验逻辑,白等了 8 秒多。问题根因就这样通过流量链路上的耗时标记定位出来,全程没有改过一行后端代码。这类问题如果只靠日志,往往只能看到两端时间戳,中间环节的耗时完全是黑的。Mitmweb 的脚本注入能力相当于在链路上打点,配合-w保存的原始流量,排查后还能整理成一份带证据的报告。4.4 离线文件的分发与协作.mitm文件体积通常很小,传给别人很方便。同事拿到文件后执行:mitmweb --read-file trace_20250101.mitm --web-port 8083就能在浏览器里看到和我当时完全一样的界面,包括每个请求的时间顺序、响应状态、Header、Body,不需要重新抓包。这种离线协作方式在很多排查场景里比截图和日志更靠谱,因为所有原始信息都在,观看者可以自己去验证。5. 那些翻文档才发现的坑:重放失败、大文件处理与生产规范5.1 重放请求怎么总是不成功Web 控制台里每条流量右键都可以重放,看起来很简单,但我在实际使用中经常被它坑到。最典型的失败原因是接口依赖动态签名:重放时请求头里的时间戳和 token 已经过期,服务端自然拒绝。这种问题跟 Mitmweb 参数无关,而是需要配合脚本在重放前更新请求头。第二个坑是 HTTP/2 连接复用。有些服务端对同一连接上的重放请求处理得不好,表现为第一次重放成功,第二次开始失败。这时可以在启动参数里加--set http2=false,强制走 HTTP/1.1 重放,成功率会高很多。第三个坑是重放只重发请求,不会重新建立完整的 TLS 会话上下文。如果服务端对会话状态敏感,重放请求可能被判定为异常。我的经验是:重放适合验证“同样的请求是否会得到同样的响应”,不适合直接当作压测工具,更不要把重放结果当作线上问题已经修复的证据。5.2 大文件与长连接会让内存暴涨在线抓包默认会把响应体完整读进内存,如果调试的是视频流或大文件下载接口,机器内存会肉眼可见地涨上去。处理办法就是前面提到的:mitmweb --set stream_large_bodies=1m这个参数的意思是超过 1MB 的响应体不再整体缓存,而是边收边转发。代价是 Web 控制台里看不到完整的消息体,但状态码和耗时信息仍然保留。对大文件接口,我最关心的是耗时和状态,消息体爱看不看。所以这个取舍很划算。长连接超时同样值得注意。某些接口会长时间挂着,如果客户端和 Mitmweb 之间有一方主动断开,流量列表里会看到一个连接异常。这时候可以配合脚本记录连接事件,或者检查--set stream_large_bodies设置后是否改变了连接生命周期。这类问题定位起来比较费劲,建议一开始就定好哪些接口需要流式处理。5.3 安全和规范:不要在生产环境抓包Mitmweb 的--listen-host 0.0.0.0组合很实用,但也意味着同一局域网内任何设备都能把流量指向你。安全习惯上我给自己定了几个规矩:默认只绑127.0.0.1,只有真机调试时才临时用0.0.0.0,用完立刻退出。抓到的流量文件不要随手放在共享目录,里面很可能有登录态 Cookie、Token 等敏感信息。.mitm文件传给别人时,优先用支持加密的通道。不在生产环境直接部署 Mitmweb,即使是只读分析也要先用导出的文件在本地处理。这些规矩不是教条,而是我确实见过有人为了图方便,把监听端口暴露在办公网里,结果被抓到了大量业务敏感信息。调试工具的便利性必须放在可控范围内。5.4 把常用参数封装成启动脚本命令行参数记太多容易乱,我建议把常用组合写成脚本,团队里也能统一使用。一个最小化的封装示例:#!/usr/bin/env bash # start_capture.sh PORT="${1:-8899}" SAVE_FILE="${2:-trace_$(date +%Y%m%d_%H%M).mitm}" mitmweb \ --listen-host 0.0.0.0 \ --listen-port "$PORT" \ --web-port 8081 \ --set block_global=false \ --set stream_large_bodies=1m \ -w "$SAVE_FILE"用的时候:chmod +x start_capture.sh ./start_capture.sh 8899脚本化之后还有一个好处:不同项目之间可以共用同一套参数基线,新同事上手时不用再去翻一长串文档。我自己会把脚本放在项目的scripts/目录下,和代码一起维护。参数变更会有版本记录,方便回溯是哪次改动导致抓包行为变化。最后再分享两个小技巧第一,先想清楚这次抓包的目标,再决定参数。只抓本机 Web 接口,不需要--listen-host 0.0.0.0;只分析已有文件,用--read-file就够了;要改请求,就加--scripts。需求越明确,命令越简单,排错越容易。第二,可以把最常用的参数做成 shell alias,省去每次敲一大串:alias mweb='mitmweb --listen-port 8899 --web-port 8081 --set block_global=false -w trace_$(date +%Y%m%d).mitm'我在实际使用中最大的体会是,Mitmweb 的参数本身并不复杂,复杂的是怎么根据场景选对参数,以及遇到 HTTPS 证书、重放失败、大文件内存暴涨这些具体问题时能快速定位到对应开关。把这些参数组合背下来,调试接口的效率真的能翻倍。
最近排查一个某内部系统的登录超时问题,后端日志显示请求已经到了,但响应时间一直波动,浏览器 F12 只能看到 Web 端的流量,App 里真正传参的情况完全看不见。折腾到半夜,最后是靠 Mitmweb 把完整的请求响应链捞出来,才定位到是某个响应头被上游网关吃掉,导致客户端一直重试。这件事让我意识到,Mitmweb 的参数配置才是真正决定调试效率的东西。不少同学是直接敲mitmweb就开抓,等遇到端口占用、证书不生效、脚本注入失败、流量文件打不开这些情况,才开始一页页翻文档。这篇文章就围绕 Mitmweb 参数配置来写,把我自己实际用下来最有价值的参数组合、常见坑和排查思路一次讲清楚。适合前后端开发、测试同学,以及做接口联调或流量分析的同学参考,不管你是第一次接触还是已经用过一段时间,应该都能从中找到能直接抄走的命令行。1. 先把运行环境收拾利索:安装版本与证书信任这两道坎1.1 安装方式与 Python 版本Mitmweb 是 mitmproxy 工具族里的 Web 界面版本,安装走 Python 包管理最方便。我推荐在虚拟环境里安装,避免污染系统里其他 Python 项目的依赖:python3 -m venv .venv source .venv/bin/activate pip install mitmproxy安装完用版本号验证一下:mitmweb --version如果能看到类似Mitmproxy: 10.x.x的输出,说明装好了。这里的版本号很重要,后面不少参数的行为在不同大版本之间会有差异。我自己就遇到过照着老教程写--set websocket=true,结果新版直接不识别的情况。提示:不建议用sudo pip install全局安装,很容易把系统 Python 环境搞乱。虚拟环境是成本最低的隔离方案。1.2 证书信任:为什么 HTTPS 流量全是乱码第一次启动 Mitmweb 后抓取 HTTPS 请求,会发现流量列表里都是解密失败或连接错误,这不是工具坏了,而是它作为中间层需要被客户端信任。Mitmweb 首次运行会在用户目录生成一套本地 CA 证书,路径大概是~/.mitmproxy/mitmproxy-ca.pem。要把这套证书装到测试设备上,最省事的办法是直接用 Web 控制台自带的证书下载页。电脑本机调试:访问http://127.0.0.1:8081/cert/pem下载证书,双击导入系统信任区。手机真机调试:手机连上同一局域网后,访问http://<电脑IP>:8081/cert/,按设备类型下载安装。Android 7 及以上版本,很多应用默认不信任用户 CA 证书,即使你安装了也仍然抓不到,需要在应用的networkSecurityConfig里显式允许用户 CA,或者用调试包。iOS 设备安装描述文件后,还要去“关于本机 > 证书信任设置”里打开完全信任开关,否则 Safari 会报证书错误。这一步经常被忽略,导致很多人误以为工具不支持 HTTPS。实际上只要证书信任了,剩下的参数配置才有意义。1.3 用一个最小命令验证环境证书弄完,先别急着上复杂参数,跑一个最小命令确认环境正常:mitmweb --listen-port 8899 --web-port 8081启动后浏览器打开http://127.0.0.1:8081,能看到消息列表和漏斗过滤栏。这时候随便访问一个 HTTP 页面,消息列表里应该出现对应记录。能走到这一步,说明安装、端口、Web 控制台三个环节都通了,再往下加参数就不会互相甩锅。2. 上手第一个参数组合:流量入口和控制台端口是两个独立维度2.1 端口参数各自管什么Mitmweb 启动后虚线监听的是两个端口,很多人把它们混为一谈,导致怎么调都不对。这里我建议先记住:--listen-port管的是流量入口,客户端要把流量转发到这个端口;--web-port管的是调试界面,浏览器通过这个端口看抓包结果。参数默认值作用--listen-host127.0.0.1流量入口绑定的地址--listen-port8080客户端流量转发过来的端口--web-host127.0.0.1Web 控制台绑定的地址--web-port8081浏览器访问控制台的端口默认情况下,mitmweb会把流量入口和控制台都绑在127.0.0.1,也就是只允许本机访问。这个设计是安全的,但坑也在这:手机真机调试时,如果你只改端口不改成0.0.0.0,手机根本连不上你的电脑。2.2 最常见的本地真机抓包组合我自己最常用的基础组合是:mitmweb --listen-host 0.0.0.0 --listen-port 8899 --web-port 8081拆开来看,--listen-host 0.0.0.0表示流量入口监听所有网卡,这样同一局域网内的手机才能把流量指过来。--listen-port 8899是手机端要填的端口,我不用默认 8080 是因为开发机上这个端口被各种服务占用的概率太高。--web-port 8081保持默认,方便记忆。手机端在 Wi-Fi 设置里把“HTTP 转发”或“手动配置”指向电脑的局域网 IP 和 8899 端口,然后浏览器打开http://<电脑IP>:8081就能看到手机流量实时出现。注意这时候 Web 控制台绑定了127.0.0.1,所以只有电脑本机能访问,手机访问不到也没关系,这反而是更安全的配置。2.3 端口占用与防火墙排查如果启动时提示Address already in use,说明端口已经被别的进程占用了。排查命令:lsof -i :8899 netstat -tlnp | grep 8899找到占用进程后,要么换端口,要么先处理掉旧进程。还有一个容易被忽略的点是系统防火墙:本机抓包通常不受影响,但手机真机抓包时,如果电脑开启了防火墙,即使监听地址是0.0.0.0,外部连接也可能被拦掉。需要放行对应端口,或者把电脑加入同一个无隔离网络。注意:公司办公网络里可能还有更严格的网络准入策略,真机抓包前先确认这类限制,否则定位半天也连不上。3. 从“只开眼”到“能干活”:脚本注入、运行期选项与流量落盘3.1--scripts参数:给请求和响应打补丁抓包只能看数据,改数据要靠脚本注入。Mitmweb 支持用 Python 脚本拦截和修改请求响应,这在调试接口异常时特别有用。举个我实际写过的例子:# addon.py from mitmproxy import http def request(flow: http.HTTPFlow) -> None: if "login" in flow.request.pretty_url: flow.request.headers["X-Debug-Mode"] = "1" def response(flow: http.HTTPFlow) -> None: if "login" in flow.request.pretty_url: flow.response.headers["X-Backend-Latency"] = str( flow.response.timestamp_end - flow.response.timestamp_start ) print(flow.request.pretty_url, flow.response.status_code)启动时带上:mitmweb --listen-port 8899 --web-port 8081 --scripts addon.py这段脚本做的事情是:请求到达时给登录接口加一个调试头,响应返回时把后端处理耗时写进自定义响应头。配合 Web 控制台直接能看到,不用再翻后端日志去算耗时。脚本里的事件函数名是约定好的,request和response分别对应每个请求和响应的回调,比在控制台里一条条改更高效。3.2--set参数:运行时选项才是进阶的关键--set参数负责修改工具运行时的内部配置,相当于把 Web 界面“选项”页里的开关直接写成命令行。我常用的几个选项如下:选项作用适用场景block_global=false允许转发到外部地址的请求浏览器访问公网接口时,默认会被拦截stream_large_bodies=1m超过 1MB 的响应体按流式处理,不整体缓存在内存里抓取大文件下载、视频流接口ssl_insecure=true忽略上游证书校验错误本地测试自签名证书的后端服务connection_strategy=lazy延迟建立到目标服务器的连接流量很大时减少无谓的连接开销http2=false关闭 HTTP/2 支持重放请求时部分服务端对 HTTP/2 兼容不佳举个例子,默认情况下浏览器访问外部 HTTPS 网站时,Mitmweb 会因为安全策略拦截请求,很多新手第一次抓包发现列表里什么都没有,就是这个问题。排查时可以记住:消息列表里出现400或502,同时日志提示block_global,就直接在启动命令里加--set block_global=false。3.3 把流量保存下来:-w/--save-stream-file调试接口最怕“刚才明明抓到了,但忘了留证据”。-w参数可以把在线流量实时写入一个文件:mitmweb --listen-port 8899 --web-port 8081 -w trace_$(date +%Y%m%d).mitm这样生成的.mitm文件包含请求、响应、证书信息、时间戳等完整上下文,比浏览器导出的 HAR 信息更全。我通常在开始抓包前就把-w带上,哪怕用不上,事后也能回溯。3.4 离线查看流量:--read-file如果已经有一份.mitm文件,不需要重新抓包就能用 Web 控制台分析:mitmweb --read-file trace_20250101.mitm --web-port 8082这个模式非常有用,别人发你一份流量文件,你直接起一个离线 Web 控制台慢慢看,不会干扰正在运行的服务。因为不会启动新的流量入口,所以只需要指定 Web 端口即可。对比一下:在线模式是“边抓边看”,离线模式是“抓完再看”,后者遇到复杂问题可以慢慢分析,不用逼着用户一直开着电脑。4. 几个高频工作流的参数组合:真机抓包、后端联调、离线分析4.1 真机 App 抓包的标准起手式真机抓包需要把流量入口暴露到局域网,同时又要考虑外部地址访问和文件保存,我的标准命令是这样:mitmweb \ --listen-host 0.0.0.0 \ --listen-port 8899 \ --web-port 8081 \ --set block_global=false \ --set stream_large_bodies=1m \ -w trace_$(date +%Y%m%d_%H%M).mitm四个关键点:第一,--listen-host 0.0.0.0让手机能连入;第二,block_global=false避免访问公网接口时被拦截;第三,stream_large_bodies=1m避免图片或视频接口把内存撑爆;第四,-w实时落盘。这套组合覆盖了我 90% 的移动端排查场景,跑起来之后手机正常操作十分钟,基本想要的数据都能抓到。有一个容易忽略的细节:手机屏幕熄灭后,Wi-Fi 可能进入省电休眠,导致流量中断。抓包时建议在手机开发者选项里开启“不锁定屏幕”,或者至少保持屏幕常亮。4.2 后端联调时如何只关注特定接口抓包数据一大就难定位,尤其后端联调时前端页面会同时产生几十个静态资源请求。Mitmweb 的 Web 控制台左上角有过滤栏,支持~u、~q、~s等过滤表达式。比如只留下 URL 中包含api的请求:~u /api/只留下响应状态码是 500 的请求:~s 500把两个条件组合在一起:~u /api/ & ~s 500过滤只是控制台显示层面的事,不会影响抓包内容。如果确实想彻底忽略某些域名,可以在启动时用--ignore-hosts参数,例如:mitmweb --ignore-hosts 'my-internal.test'被忽略的地址不会出现在列表里,能大幅减少噪音。我用下来感觉过滤栏更灵活,因为不需要重启就能改条件,适合调试过程中不断调整关注范围。4.3 一次真实的超时问题定位过程这里用一个虚构但很典型的例子说明参数组合怎么发挥作用。某天同事反馈一个登录接口偶发超时,我按上面真机命令起好环境,手机把流量指到 8899,十几分钟后就抓到了失败请求。看响应耗时发现接近 10 秒,正常应该 200 毫秒。进一步用脚本注入把X-Backend-Latency写进响应头,很快就确认耗时发生在后端应用层,而不是网络链路。继续看请求头发现客户端带了一个过大的Cookie,后端在处理时触发了某个校验逻辑,白等了 8 秒多。问题根因就这样通过流量链路上的耗时标记定位出来,全程没有改过一行后端代码。这类问题如果只靠日志,往往只能看到两端时间戳,中间环节的耗时完全是黑的。Mitmweb 的脚本注入能力相当于在链路上打点,配合-w保存的原始流量,排查后还能整理成一份带证据的报告。4.4 离线文件的分发与协作.mitm文件体积通常很小,传给别人很方便。同事拿到文件后执行:mitmweb --read-file trace_20250101.mitm --web-port 8083就能在浏览器里看到和我当时完全一样的界面,包括每个请求的时间顺序、响应状态、Header、Body,不需要重新抓包。这种离线协作方式在很多排查场景里比截图和日志更靠谱,因为所有原始信息都在,观看者可以自己去验证。5. 那些翻文档才发现的坑:重放失败、大文件处理与生产规范5.1 重放请求怎么总是不成功Web 控制台里每条流量右键都可以重放,看起来很简单,但我在实际使用中经常被它坑到。最典型的失败原因是接口依赖动态签名:重放时请求头里的时间戳和 token 已经过期,服务端自然拒绝。这种问题跟 Mitmweb 参数无关,而是需要配合脚本在重放前更新请求头。第二个坑是 HTTP/2 连接复用。有些服务端对同一连接上的重放请求处理得不好,表现为第一次重放成功,第二次开始失败。这时可以在启动参数里加--set http2=false,强制走 HTTP/1.1 重放,成功率会高很多。第三个坑是重放只重发请求,不会重新建立完整的 TLS 会话上下文。如果服务端对会话状态敏感,重放请求可能被判定为异常。我的经验是:重放适合验证“同样的请求是否会得到同样的响应”,不适合直接当作压测工具,更不要把重放结果当作线上问题已经修复的证据。5.2 大文件与长连接会让内存暴涨在线抓包默认会把响应体完整读进内存,如果调试的是视频流或大文件下载接口,机器内存会肉眼可见地涨上去。处理办法就是前面提到的:mitmweb --set stream_large_bodies=1m这个参数的意思是超过 1MB 的响应体不再整体缓存,而是边收边转发。代价是 Web 控制台里看不到完整的消息体,但状态码和耗时信息仍然保留。对大文件接口,我最关心的是耗时和状态,消息体爱看不看。所以这个取舍很划算。长连接超时同样值得注意。某些接口会长时间挂着,如果客户端和 Mitmweb 之间有一方主动断开,流量列表里会看到一个连接异常。这时候可以配合脚本记录连接事件,或者检查--set stream_large_bodies设置后是否改变了连接生命周期。这类问题定位起来比较费劲,建议一开始就定好哪些接口需要流式处理。5.3 安全和规范:不要在生产环境抓包Mitmweb 的--listen-host 0.0.0.0组合很实用,但也意味着同一局域网内任何设备都能把流量指向你。安全习惯上我给自己定了几个规矩:默认只绑127.0.0.1,只有真机调试时才临时用0.0.0.0,用完立刻退出。抓到的流量文件不要随手放在共享目录,里面很可能有登录态 Cookie、Token 等敏感信息。.mitm文件传给别人时,优先用支持加密的通道。不在生产环境直接部署 Mitmweb,即使是只读分析也要先用导出的文件在本地处理。这些规矩不是教条,而是我确实见过有人为了图方便,把监听端口暴露在办公网里,结果被抓到了大量业务敏感信息。调试工具的便利性必须放在可控范围内。5.4 把常用参数封装成启动脚本命令行参数记太多容易乱,我建议把常用组合写成脚本,团队里也能统一使用。一个最小化的封装示例:#!/usr/bin/env bash # start_capture.sh PORT="${1:-8899}" SAVE_FILE="${2:-trace_$(date +%Y%m%d_%H%M).mitm}" mitmweb \ --listen-host 0.0.0.0 \ --listen-port "$PORT" \ --web-port 8081 \ --set block_global=false \ --set stream_large_bodies=1m \ -w "$SAVE_FILE"用的时候:chmod +x start_capture.sh ./start_capture.sh 8899脚本化之后还有一个好处:不同项目之间可以共用同一套参数基线,新同事上手时不用再去翻一长串文档。我自己会把脚本放在项目的scripts/目录下,和代码一起维护。参数变更会有版本记录,方便回溯是哪次改动导致抓包行为变化。最后再分享两个小技巧第一,先想清楚这次抓包的目标,再决定参数。只抓本机 Web 接口,不需要--listen-host 0.0.0.0;只分析已有文件,用--read-file就够了;要改请求,就加--scripts。需求越明确,命令越简单,排错越容易。第二,可以把最常用的参数做成 shell alias,省去每次敲一大串:alias mweb='mitmweb --listen-port 8899 --web-port 8081 --set block_global=false -w trace_$(date +%Y%m%d).mitm'我在实际使用中最大的体会是,Mitmweb 的参数本身并不复杂,复杂的是怎么根据场景选对参数,以及遇到 HTTPS 证书、重放失败、大文件内存暴涨这些具体问题时能快速定位到对应开关。把这些参数组合背下来,调试接口的效率真的能翻倍。