作者:龙际妙
标签:PAC / Whistle / VPN / 网络代理 / 开发效率
一、多代理的日常
作为一名前端/后端/测试工程师,你是否也面临这样的场景?
- 科学上网需要开启 VPN(如 ClashX)
- 开发联调时需要使用 Whistle 本地代理前后端环境
- 抓包调试时习惯用 Charles、Fiddler、mimtproxy等其他代理工具
- 浏览器插件(如 SwitchyOmega):按需切换代理配置
这些代理工具往往都通过"设置为系统代理"提供服务,虽然提升了使用便利性,但问题也随之而来:
👉 系统同一时间只能生效一个代理设置,多个代理同时开启时就容易互相"打架"。
笔者之前就遇到过这样的场景:
::: block-1 本地起了 Whistle(监听 8899 端口)进行工作开发,同时 ClashX 设置为系统代理科学上网。
在Chrome 里通过 SwitchyOmega 插件使网络请求走 Whistle,但是发现在Chrome浏览器里就无法再使用谷歌搜索,遇到这种问题大家也是八仙过海:
-
1.Chrome只用来开发,再开一个浏览器(Edge)进行搜索
这种方式多个浏览器互相切换成本高,多窗口来回切换极其浪费时间
-
2.通过切换 SwitchyOmega 情景代理模式
这种手动切换繁琐,容易忘记,给自己创造心智负担
-
3.使用 SwitchyOmega 里的auto switch模式
- 公司内网域名 → 走Whistle
- 其他 → VPN
无需频繁切换,规则灵活,能满足大部分人的需求,但是SwitchyOmega 仅限当前浏览器生效,无法作用于其他浏览器和应用。
有没有更通用、统一且优雅的解决方案?有,那就是本篇的主角 ------ PAC(Proxy Auto-Config)。 :::
二、什么是 PAC?
PAC 全称是 Proxy Auto-Config,是浏览器和操作系统支持的一种自动代理规则机制。
就是一个
.pac
文件其核心是一个 JavaScript 函数,用来决定网页浏览请求(HTTP、HTTPS,和 FTP)应当直连目标地址,还是被转发给一个代理服务器通过代理连接。
PAC 文件中的核心 JavaScript 函数通常是这样定义的:
js
function FindProxyForURL(url, host) {
if (shExpMatch(host, "*.zhuanzhuan.com"))
return "PROXY 127.0.0.1:8899"; // Whistle
if (shExpMatch(host, "*.google.com"))
return "PROXY 127.0.0.1:7890"; // Clash
return "DIRECT";
}
每次访问网站时,浏览器或操作系统会调用这个函数,根据你访问的 URL 和 域名,返回一个指令:
是走某个代理(PROXY),还是直连(DIRECT)。
这就意味着:你可以根据目标网站,灵活指定走哪一个代理服务,而不用频繁切换配置。
谁支持 PAC?
SwitchyOmega可以直接通过新建PAC模式,通过网络地址加载文件或者本地编写脚本。

但是之前提到SwitchyOmega作为一个浏览器插件,它的代理作用域只有当前浏览器,像在其他的浏览器内和应用内的请求或者都是不起作用的。要想让 PAC 文件在系统级别生效,我们需要将它配置在操作系统的网络代理设置中:
以macOS为例:系统设置->Wi-Fi(当前连接WI-FI)->详细信息->代理 macOS只支持从服务器加载配置,Windows支持从本地加载和服务器远程加载文件。
三、实战案例:用 PAC 管理多个代理服务
下面这个示例,可以根据请求的域名自动选择合适的代理,而且PAC提供了容灾兜底机制,若无法与其中指定的代理服务器建立连接。在这种情况下,将使用下一个代理配置。
js
const DIRECT = 'DIRECT'; // 直接连接,不使用代理
const DEFAULT = 'PROXY 127.0.0.1:7890; DIRECT'; // 默认策略,走 ClashX 代理,若ClashX代理无法响应则自动改为直连
const WHISTLE = 'PROXY 127.0.0.1:8899'; // Web 开发用 Whistle 代理
// 域名匹配规则(按顺序匹配,先匹配先生效)
const config = [
{ proxy: DIRECT, pattern: /^::1$/ }, // 本地地址,不走代理
{ proxy: DIRECT, pattern: /^127\.0\.0\.1$/ },
{ proxy: DIRECT, pattern: /^localhost$/ },
{ proxy: WHISTLE, pattern: /\.caihuoxia\.com$/ }, // 业务域名,走Whistle代理
{ proxy: WHISTLE, pattern: /\.zhuanzhuan\.com$/ },
{ proxy: WHISTLE, pattern: /\.example\.com$/ }
];
function FindProxyForURL(url, host) {
for (let i = 0; i < config.length; i++) {
if (config[i].pattern.test(host)) {
return config[i].proxy;
}
}
return DEFAULT; // 未命中规则时走默认策略
}
如果你本地还启用了其他代理工具,也可以在 PAC 文件中为它们添加对应规则。你也可以根据自己的实际需求自由扩展配置,编写出适合自己开发环境的专属 PAC 文件。
此外,PAC 文件内置了一些实用方法(如 dnsDomainIs、shExpMatch、isInNet 等),可以帮助你实现更灵活的匹配逻辑,具体可参考官方文档。
📚 官方文档地址(复制打开): developer.mozilla.org/zh-CN/docs/...
四、搭建本地服务器托管 PAC (支持热更新)
现在我们已经有了一个完善的 PAC 文件,接下来我们需要让它在 macOS 上"动"起来 ------ 也就是将它托管在一个可访问的地址中。最简单的方式是本地搭建服务器,设置支持热更新,便于我们在开发中随时修改规则。当然也可以托管到远程服务器上访问。
在这里通过创建一个本地服务器来托管 .pac 文件。
✅ 示例代码(Node.js 实现)
javascript
const http = require('http');
const fs = require('fs');
const path = require('path');
// === 配置 ===
const PAC_PATH = path.resolve(__dirname, './proxy.pac'); // PAC 文件的绝对路径
const PORT = 6001; // 本地服务器监听端口
const HOST = '127.0.0.1'; // 监听地址(仅本地)
let pacContent = ''; // 缓存当前的 PAC 文件内容
// === 初始化加载 PAC 文件 ===
function loadPACFile() {
try {
pacContent = fs.readFileSync(PAC_PATH, 'utf8'); // 读取 PAC 文件内容
console.log(`[PAC] 已加载 PAC 文件:${PAC_PATH}`);
} catch (err) {
console.error('[PAC] 加载 PAC 文件失败:', err.message);
}
}
// === 监听 PAC 文件变更,实现热更新 ===
function watchPACFile() {
fs.watch(PAC_PATH, (eventType) => {
if (eventType === 'change') {
console.log('[PAC] 检测到文件变化,重新加载...');
loadPACFile();
}
});
}
// === 启动 HTTP 服务,提供 PAC 文件 ===
function startServer() {
http
.createServer((req, res) => {
if (req.url === '/proxy.pac') {
// 请求路径为 /proxy.pac 时返回 PAC 内容
res.writeHead(200, {
'Content-Type': 'application/x-ns-proxy-autoconfig'
});
res.end(pacContent);
console.log(`[PAC] 已响应 PAC 请求:${req.connection.remoteAddress}`);
} else {
// 其他路径返回 404
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
})
.listen(PORT, HOST, () => {
console.log(`[PAC] 本地服务器已启动:http://${HOST}:${PORT}/proxy.pac`);
});
}
// === 主程序入口 ===
loadPACFile(); // 加载初始 PAC 内容
watchPACFile(); // 启动监听文件变化
startServer(); // 启动本地 HTTP 服务
这样,我们就完成了一个支持热更新的本地 PAC 服务,方便在开发过程中灵活修改和使用。如果有多个设备或需要远程共享,也可以将它托管到内网或公网服务器上,根据实际需求灵活调整即可。
五、总结
最终,我们通过编写 PAC 文件,配合本地 HTTP 服务进行托管,实现了按域名精确控制代理走向,不仅解决了多代理冲突的问题,也让代理配置更集中、统一、高效。
通过这种方式使用 PAC 的优雅之处:
- 无需频繁手动切换:规则自动匹配,专注工作内容。
- 系统级生效:不仅浏览器,连Postman以及其他等应用都能统一使用。
- 动态返回代理地址:内部逻辑完全可控,可使用 JS 判断域名、路径甚至时间段。
- 避免代理冲突:通过统一规则自动分流,多个代理工具协同工作更稳定。
- 跨平台支持:macOS、Windows 都原生支持。
使用 PAC 不是简单的代理方式替换,更是一次开发效率和体验的提升。