Mastering Proxy Auto-Config (PAC): Seamlessly Manage Multiple Proxies for Development
This guide explains how to use Proxy Auto‑Config (PAC) files to intelligently route traffic through different proxy tools like ClashX, Whistle, and SwitchyOmega, covering the underlying JavaScript function, configuration examples, and a Node.js server for hot‑reloading the PAC file.
1. Daily Proxy Challenges
As a front‑end/back‑end/test engineer you may need VPN (e.g., ClashX) for scientific internet access, Whistle for local proxy during development, and other tools like Charles, Fiddler, mitmproxy for packet capture. Browser extensions such as SwitchyOmega let you switch proxy configurations on demand.
VPN (ClashX) for internet access
Whistle for local development proxy
Charles/Fiddler/mitmproxy for debugging
Browser extensions (SwitchyOmega) to toggle proxies
These tools usually set themselves as the system proxy, which is convenient but only one proxy can be active at a time, causing conflicts when multiple proxies are enabled.
Example: Running Whistle on port 8899 while ClashX is set as the system proxy. Using SwitchyOmega to route Chrome traffic through Whistle makes Google Search unavailable in Chrome, leading to workarounds such as opening a separate browser for search, manually switching SwitchyOmega profiles, or using its auto‑switch mode.
SwitchyOmega’s auto‑switch can direct internal domain traffic to Whistle and other traffic to VPN, but it only works within the current browser.
2. What is PAC?
PAC (Proxy Auto‑Config) is a mechanism supported by browsers and operating systems that uses a JavaScript function to decide whether a request should go directly or through a proxy.
A .pac file contains a JavaScript function that returns a proxy directive. The core function typically looks like:
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";
}The browser calls this function for each request and receives either a PROXY or DIRECT directive, allowing flexible routing without manual switching.
Who supports PAC?
SwitchyOmega can load a PAC file from a URL or a local script, but its effect is limited to the browser.
To apply a PAC file system‑wide, configure it in the OS network proxy settings. On macOS, this is done via System Settings → Wi‑Fi → Details → Proxy. macOS only supports loading PAC files from a server, while Windows supports both local and remote files.
3. Practical Example: Managing Multiple Proxies with PAC
The following example selects a proxy based on the domain and provides a fallback mechanism.
const DIRECT = 'DIRECT'; // direct connection
const DEFAULT = 'PROXY 127.0.0.1:7890; DIRECT'; // ClashX with fallback
const WHISTLE = 'PROXY 127.0.0.1:8899'; // Whistle for development
// Pattern rules (first match wins)
const config = [
{ proxy: DIRECT, pattern: /^::1$/ }, // local address
{ proxy: DIRECT, pattern: /^127\.0\.0\.1$/ }, // loopback
{ proxy: DIRECT, pattern: /^localhost$/ }, // localhost
{ proxy: WHISTLE, pattern: /\.caihuoxia\.com$/ }, // business domain
{ 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; // default policy if no rule matches
}You can extend the rules to cover other proxy tools as needed, and PAC also provides built‑in helpers such as dnsDomainIs, shExpMatch, isInNet, etc.
4. Hosting the PAC File Locally with Hot Reload
To make the PAC file active, host it on a local HTTP server that supports hot reloading.
Example Node.js server:
const http = require('http');
const fs = require('fs');
const path = require('path');
const PAC_PATH = path.resolve(__dirname, './proxy.pac');
const PORT = 6001;
const HOST = '127.0.0.1';
let pacContent = '';
function loadPACFile() {
try {
pacContent = fs.readFileSync(PAC_PATH, 'utf8');
console.log(`[PAC] Loaded ${PAC_PATH}`);
} catch (err) {
console.error('[PAC] Failed to load:', err.message);
}
}
function watchPACFile() {
fs.watch(PAC_PATH, (eventType) => {
if (eventType === 'change') {
console.log('[PAC] File changed, reloading...');
loadPACFile();
}
});
}
function startServer() {
http.createServer((req, res) => {
if (req.url === '/proxy.pac') {
res.writeHead(200, { 'Content-Type': 'application/x-ns-proxy-autoconfig' });
res.end(pacContent);
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
}).listen(PORT, HOST, () => {
console.log(`[PAC] Server running at http://${HOST}:${PORT}/proxy.pac`);
});
}
loadPACFile();
watchPACFile();
startServer();This server serves the PAC file and automatically reloads it when the file changes, making proxy adjustments instantaneous during development.
5. Summary
No manual switching: rules match automatically.
System‑wide effect: works for browsers, Postman, and other apps.
Dynamic proxy selection: logic can consider domain, path, or time.
Avoids proxy conflicts by centralizing rules.
Cross‑platform support: macOS and Windows natively handle PAC.
Using PAC transforms proxy management from a cumbersome manual task into an efficient, unified solution that boosts development productivity.
大转转FE
Regularly sharing the team's thoughts and insights on frontend development
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
