Testing PAC files in CI with pactester
A PAC file is code that routes every request in your organisation, yet it is usually edited by hand and deployed without a test. A small regression test with pactester catches the classic "one line changed, half the traffic goes direct" before it reaches users.
1. Write down what the PAC should do
A plain text file, pac-tests.txt, with one URL and the expected answer per line:
http://intranet/ DIRECT
https://wiki.corp.example/ DIRECT
http://10.1.2.3/ DIRECT
http://evilcorp.example/ PROXY proxy1.corp.example:8080; PROXY proxy2.corp.example:8080
https://www.example.com/ PROXY proxy1.corp.example:8080; PROXY proxy2.corp.example:8080
Include the cases that break PAC files in practice: look-alike domains, IP literals, plain host names, and a URL that matches no rule, which tests the default route. The testing guide has a fuller list.
2. Run it with pactester
pactester -p <file> -u <url> prints the proxy string for one URL, and -c <ip> sets what
myIpAddress() returns. With -f <file> it reads one URL per line and prints url : result. Our
own lab runs pactester this way (source: lab, 2026-10-04).
#!/bin/sh
# pac-test.sh - fail when proxy.pac routes a test URL differently than expected
pac=${1:-proxy.pac}; tests=${2:-pac-tests.txt}; fail=0
while read -r url expected; do
case "$url" in ''|'#'*) continue ;; esac
got=$(pactester -p "$pac" -u "$url" -c 10.1.2.100)
if [ "$got" != "$expected" ]; then
echo "FAIL $url"; echo " expected: $expected"; echo " got: $got"; fail=1
fi
done < "$tests"
exit $fail
3. Or with the pacparser Python module
import pacparser, sys
pacparser.init()
pacparser.setmyip("10.1.2.100")
pacparser.parse_pac_file("proxy.pac")
fail = 0
for line in open("pac-tests.txt"):
if not line.strip() or line.startswith("#"):
continue
url, expected = line.split(None, 1)
host = url.split("/")[2].split(":")[0]
got = pacparser.find_proxy(url, host)
if got != expected.strip():
print(f"FAIL {url}\n expected: {expected.strip()}\n got: {got}")
fail = 1
pacparser.cleanup()
sys.exit(fail)
init, setmyip, find_proxy and cleanup are the calls our parity tests use against the real
library (source: code, 2026-10-04). Install it with pip install pacparser.
4. Know what pactester does not see
pactester is one engine, pacparser. In the browsers:
- Chrome removes the path and query of
https://URLs before calling the PAC, and Firefox removes them for every scheme. pactester passes the full URL (source: code, verified 2026-10-04). - pactester does not lower-case
host(source: code, verified 2026-10-04), so lower-case it in the PAC. - pactester resolves names with real DNS. Make sure CI’s resolver gives the answers you expect, or keep DNS functions out of the PAC (DNS in PAC files).
The full list is in the engine differences table. For the cross-engine view and the mistake checks, run the file through the online PAC file tester as well. Do that before the commit; CI is the safety net after it.
Frequently asked questions
How do I test a PAC file automatically?
Keep a file of test URLs with the expected proxy string for each, run every URL through pactester (from pacparser) or the pacparser Python module in your CI pipeline, and fail the build when an answer differs.
Does pactester behave like Chrome or Firefox?
Not exactly. pactester passes the full URL to the script, while Chrome removes the path and query of https URLs and Firefox removes them for every scheme. pactester also does not lower-case host. Keep URL-path rules out of the PAC, or test them in the browsers as well.