多代理混战?用 PAC(Proxy Auto-Config) 优雅切换代理场景

作者:龙际妙

标签: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 不是简单的代理方式替换,更是一次开发效率和体验的提升。

相关推荐
Boilermaker19928 分钟前
【Java EE】SpringIoC
前端·数据库·spring
Super Rookie9 分钟前
Spring Boot 企业项目技术选型
java·spring boot·后端
来自宇宙的曹先生11 分钟前
用 Spring Boot + Redis 实现哔哩哔哩弹幕系统(上篇博客改进版)
spring boot·redis·后端
中微子19 分钟前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript
expect7g32 分钟前
Flink-Checkpoint-1.源码流程
后端·flink
天天向上102434 分钟前
Vue 配置打包后可编辑的变量
前端·javascript·vue.js
趣多多代言人35 分钟前
从零开始手写嵌入式实时操作系统
开发语言·arm开发·单片机·嵌入式硬件·面试·职场和发展·嵌入式
00后程序员39 分钟前
Fiddler中文版如何提升API调试效率:本地化优势与开发者实战体验汇总
后端
芬兰y1 小时前
VUE 带有搜索功能的穿梭框(简单demo)
前端·javascript·vue.js
好果不榨汁1 小时前
qiankun 路由选择不同模式如何书写不同的配置
前端·vue.js