macOS 上 Chrome CDP 远程调试端口无法监听的排查与解决
macOS 上 Chrome CDP 远程调试端口无法监听的排查与解决
在 macOS 上使用 Playwright 通过 CDP(Chrome DevTools Protocol)连接已有 Chrome 浏览器时,发现
--remote-debugging-port=9222参数被静默忽略,端口始终不监听。本文记录了完整的排查过程、根本原因和解决方案。
背景
在自动化测试场景中,我们经常需要复用已登录的浏览器 session,避免每次运行脚本都重新登录内网系统。常见的做法是:
- 以
--remote-debugging-port=9222参数启动 Chrome - 通过 Playwright 的
chromium.connectOverCDP()连接到已有浏览器实例 - 直接在已登录的页面上执行自动化操作
这种方式在大多数教程和文档中都是标准做法,理论上非常简单。然而在实际操作中,我们在 macOS + Chrome 145 的环境下遇到了一个令人困惑的问题。
问题现象
环境信息
| 项目 | 版本 |
|---|---|
| 操作系统 | macOS (Darwin) |
| Chrome | 145.0.7632.117 |
| Playwright | 1.51.1 |
| Node.js | v22.x |
复现步骤
# 1. 关闭所有 Chrome 进程
killall "Google Chrome"
# 2. 以远程调试模式启动 Chrome
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
# 3. 检查端口是否在监听
lsof -i :9222
# 结果:无输出(端口未监听)
# 4. 尝试访问 CDP 接口
curl http://127.0.0.1:9222/json/version
# 结果:curl: (7) Failed to connect to 127.0.0.1 port 9222: Connection refused
令人困惑的地方
通过 ps aux | grep Chrome 可以确认 Chrome 主进程确实带有 --remote-debugging-port=9222 参数:
max 69962 /Applications/Google Chrome.app/Contents/MacOS/Google Chrome --remote-debugging-port=9222
进程参数正确,但端口就是不监听。没有任何报错弹窗,Chrome 正常打开,一切看起来都很正常——除了 CDP 不工作。
排查过程
排除网络和防火墙问题
# 检查端口是否被占用
lsof -i :9222
# 结果:无占用
# 检查 macOS 防火墙状态
/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
# Firewall is enabled. (State = 1)
# 检查 Chrome 的防火墙规则
/usr/libexec/ApplicationFirewall/socketfilterfw --listapps | grep -A2 Chrome
# /Applications/Google Chrome.app → (Allow incoming connections)
防火墙虽然开启,但 Chrome 已被允许传入连接,排除防火墙问题。
排除启动方式问题
尝试了多种启动方式,均无法开启 CDP:
# 方式1:直接执行二进制
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
# 方式2:通过 open 命令
open -a "Google Chrome" --args --remote-debugging-port=9222
# 方式3:加上 --no-first-run
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --no-first-run
以上方式结果一致:Chrome 正常启动,但 9222 端口不监听。
关键线索:stderr 输出
将 Chrome 的 stderr 重定向到文件后,发现了关键信息:
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 2>/tmp/chrome_stderr.log &
sleep 5
cat /tmp/chrome_stderr.log
输出:
DevTools remote debugging requires a non-default data directory. Specify this using --user-data-dir.
这就是根本原因。 Chrome 将这条关键信息输出到了 stderr,没有弹窗,没有任何可见提示。如果你不特意捕获 stderr,永远不会看到这条消息。
根本原因
Chrome 的安全策略变更
从 Chrome 的某个较新版本开始(至少在 145 版本中已生效),CDP 远程调试增加了一项安全限制:
使用默认用户数据目录(Default Profile)时,
--remote-debugging-port参数会被静默忽略。
这意味着:
- Chrome 会正常启动并恢复上次的标签页和 session
--remote-debugging-port=9222参数出现在进程列表中(ps aux可以看到)- 但 Chrome 不会在 9222 端口上开启 DevTools WebSocket 监听
- 唯一的提示是 stderr 中的一行日志
为什么要做这个限制
这是一项安全防护措施。Chrome 的默认用户数据目录包含用户的完整浏览数据(密码、Cookie、历史记录等)。如果允许在默认 profile 上随意开启远程调试端口,本机的任何程序都可以通过 CDP 协议:
- 读取所有打开页面的 DOM 和 JavaScript 上下文
- 获取已保存的密码和 Cookie
- 注入并执行任意 JavaScript
- 截取页面截图
要求使用非默认数据目录,相当于强制开发者创建一个隔离的调试环境,降低了默认 profile 被意外暴露的风险。
相关 Chromium 源码
在 Chromium 源码中,这个检查位于浏览器启动流程中。当 Chrome 检测到以下条件同时满足时,会跳过 CDP 端口绑定:
- 命令行包含
--remote-debugging-port - 未指定
--user-data-dir(即使用默认数据目录)
相关逻辑可参考 Chromium 源码中的 chrome/browser/devtools/remote_debugging_server.cc。
解决方案
方案一:指定独立的 user-data-dir(推荐)
# 关闭所有 Chrome 进程
killall "Google Chrome"
# 以 CDP 模式启动,指定独立的数据目录
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/chrome-debug-profile
验证 CDP 是否正常:
curl -s http://127.0.0.1:9222/json/version
成功输出:
{
"Browser": "Chrome/145.0.7632.117",
"Protocol-Version": "1.3",
"V8-Version": "14.5.201.12",
"webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/browser/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
注意事项:
- 使用新的
user-data-dir意味着全新的浏览器 profile,首次启动需要重新登录各网站 - Session 数据会保存在指定目录中,后续再用相同目录启动即可复用登录状态
- 建议使用固定路径(如
~/.chrome-debug-profile)而非/tmp目录,避免系统清理后丢失 session
# 推荐使用固定路径
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 \
--user-data-dir=$HOME/.chrome-debug-profile
方案二:复制默认 Profile 的 Cookie 到新目录
如果希望避免重新登录,可以将默认 Profile 的关键文件复制到新的数据目录:
# Chrome 默认数据目录(macOS)
CHROME_DEFAULT="$HOME/Library/Application Support/Google/Chrome"
DEBUG_PROFILE="/tmp/chrome-debug-profile"
# 创建目录
mkdir -p "$DEBUG_PROFILE/Default"
# 复制关键 session 文件
for f in Cookies "Login Data" "Web Data" Preferences "Secure Preferences"; do
cp "$CHROME_DEFAULT/Default/$f" "$DEBUG_PROFILE/Default/$f" 2>/dev/null
done
cp "$CHROME_DEFAULT/Local State" "$DEBUG_PROFILE/Local State" 2>/dev/null
# 启动 Chrome
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 \
--user-data-dir="$DEBUG_PROFILE"
⚠️ 此方式不保证 100% 有效。Chrome 的 Cookie 文件使用了操作系统级的加密(macOS Keychain),复制后可能无法正常解密。
方案三:Playwright launchPersistentContext 降级方案
如果 CDP 方案不可行,可以使用 Playwright 的 launchPersistentContext API,指定 Chrome 的用户数据目录来复用 Cookie:
import { chromium } from 'playwright';
const context = await chromium.launchPersistentContext(
'/tmp/playwright-chrome-profile',
{
headless: false,
channel: 'chrome', // 使用系统安装的 Chrome
viewport: { width: 1280, height: 900 },
}
);
const page = context.pages()[0] || await context.newPage();
这种方式由 Playwright 自行管理浏览器生命周期,不依赖 CDP,但同样需要在该 profile 中先完成一次登录。
快速诊断清单
当你遇到 CDP 连接不上时,按以下步骤排查:
| # | 检查项 | 命令 |
|---|---|---|
| 1 | Chrome 进程是否携带 CDP 参数 | ps aux | grep Chrome | grep remote-debugging |
| 2 | 端口是否在监听 | lsof -i :9222 |
| 3 | 是否有其他程序占用端口 | lsof -i :9222 |
| 4 | 查看 Chrome stderr 输出 | 启动时重定向 2>/tmp/chrome.log |
| 5 | 防火墙是否阻止 | socketfilterfw --getglobalstate |
| 6 | 是否指定了 --user-data-dir |
检查启动命令 |
总结
| 项 | 内容 |
|---|---|
| 问题 | Chrome 的 --remote-debugging-port 参数被静默忽略,CDP 端口不监听 |
| 原因 | Chrome 145+ 要求 CDP 必须配合 --user-data-dir 使用非默认数据目录 |
| 表现 | 进程参数正确但端口不 LISTEN,仅 stderr 有一行提示 |
| 解决 | 启动时加上 --user-data-dir=<路径> 参数 |
浙公网安备 33010602011771号