pactester vs Chrome, Edge, Firefox and Windows: why a PAC file passes in pactester and fails in the browser

Published

pactester is the usual way to test a proxy.pac on the command line and in CI. For most files it gives the same answer as the browsers. For some calls it does not, and then a test passes while users go to the wrong proxy. Windows adds its own engine on top.

Why the engines differ

Every PAC engine contains a small library of helper functions such as isInNet, weekdayRange and shExpMatch. In pactester, Chrome and Firefox they go back to the same code that Netscape and later Mozilla wrote in the late 1990s. pacparser, the library behind pactester and the Python module pacparser, keeps that code close to its original form. Chrome and Firefox have changed their copies over the years. Windows evaluates PAC files with its own implementation in WinHTTP, and WinINet in older applications.

None of these is a bug in the usual sense. The trouble starts when a file is tested in one engine and used in another, and the usual case is a test with pactester and users on Chrome, Edge or Firefox.

Where pactester and the browsers can disagree

  • isInNet() with a network in CIDR form such as "10.0.0.0/8", a shortened mask, a prefix length as the mask, leading zeros or a missing argument. Some of these are true in pactester and false in every browser.
  • weekdayRange(), dateRange() and timeRange() with a range that wraps around the weekend, the new year or midnight. The browsers match it, pactester never does, and timeRange(22, 6) matches nowhere.
  • myIpAddress(), which pactester takes from the machine the test runs on, while the browsers take it from the network connection.
  • dnsResolve() and every function that resolves names: the engines differ in IPv6 answers, in upper-case names and in how many lookups one run of the script may make.
  • isPlainHostName() on IPv6 addresses.
  • IP addresses written with leading zeros, in hex or as one number, such as 010.1.2.3. The browsers convert the host to the plain form before the file sees it, pactester does not, and Windows reads leading zeros as octal. Write plain addresses even where the odd form works (PAC-C031).
  • The URL itself. pactester passes the full URL to FindProxyForURL, while the browsers remove the path and query, at least for https://. A rule on the path matches in the test and not in the browser (engine differences).

Windows is a third kind of engine

Applications that use the Windows proxy settings, and Windows services, get their answer from WinHTTP’s own PAC engine, not from pactester’s code and not from a browser’s. It answers some of the calls above in a third way. Chrome and Edge do not use it; they run the file in their own engine, also on Windows. A file that has to work for browsers and Windows applications alike should stay with the plain forms of each call.

The differences on Windows go further than the calls above. dateRange() and timeRange() are always false there, and newer JavaScript such as ?. makes the whole file fail. The article Windows runs PAC files differently from Chrome and Firefox lists them, and the engine page Windows (WinHTTP) has the details.

Test the file where it will run

The way to be sure is to run your file in the engine your users have. Paste it into the PAC file tester, enter a URL and an optional client IP, and switch the engine profile between Chromium, Firefox, pacparser and the two Windows profiles. The live trace shows which tests were true or false and which line decided, and the rating flags the calls whose answer depends on the engine, such as an isInNet with a CIDR network or a time range that wraps around.

Keep pactester in your CI test as the safety net, and set the client address there with pactester -c. For problems in production, start with PAC file troubleshooting.

Frequently asked questions

Does pactester give the same results as Chrome?

For most PAC files, yes. It can differ for isInNet with a CIDR network or a short mask, for IP addresses written with leading zeros or in hex, for weekdayRange, dateRange and timeRange ranges that wrap around, for myIpAddress, for DNS lookups, for IPv6 addresses, and for the URL passed to FindProxyForURL. Test the file in a browser engine profile too, for example in the tester at findproxyforurl.net/check/.

Is pacparser wrong?

No. pacparser keeps the PAC helper functions in the form Netscape and early Mozilla wrote them. Chrome and Firefox changed their copies later. Both are consistent; they are different versions of the same functions, and a PAC file should avoid the calls where they differ.

Does Windows evaluate PAC files like Chrome or like pactester?

Like neither. Windows evaluates proxy.pac and wpad.dat with its own code in WinHTTP (and WinINet in older applications), which answers some calls differently from both. Chrome and Edge use their own engine, also on Windows.

How do I check my PAC file in several engines?

Paste it into the tester at findproxyforurl.net/check/, enter a URL, and switch the engine profile between Chromium, Firefox and pacparser. The trace shows which test and which line decided in each, and the rating flags calls whose answer depends on the engine.