▶ Download PAC File Tester
PAC basics
A PAC (Proxy Auto-Configuration) file is a JavaScript file that browsers and operating systems use to decide which proxy to use for a URL. It defines one function, FindProxyForURL(url, host).
Example and return values
It is often named proxy.pac or wpad.dat. The browser calls the function for every URL it opens and connects as the returned string says.
function FindProxyForURL(url, host) {
if (isPlainHostName(host)) return "DIRECT";
if (dnsDomainIs(host, ".example.com")) return "DIRECT";
return "PROXY proxy.example.net:8080; DIRECT";
}
url is the full URL (for example http://www.example.com/a/b); host is just the host name (www.example.com, lower case, without the port)
- The return value is
"DIRECT" (no proxy) or "PROXY host:port". Separate several with ; and the next one is used when the previous one is unavailable
- Some browsers also accept
SOCKS host:port, HTTPS host:port and others
How to use
- Download
pac-tester-en.html from the download page and double-click it
- Paste your PAC into the PAC file box on the left
- In URLs to test on the right, write one URL per line
- It evaluates as you type. The Results table shows the return value for each URL
More on the screen and the evaluation settings
A Japanese version of the tool (pac-tester.html) is also available.
- Choose a .pac file with the Open button or drag and drop it onto the page
- Without
http:// a URL line is treated as http. Lines starting with # are comments. The list loads from and saves to a .txt file
- Ctrl+Enter or the Evaluate button evaluates at once
- The coloured buttons above the results are the counts for each return value; click one to show only those rows. Filter by text, show only errors and warnings, or save as CSV
- Problems found in the script are listed under Syntax and logic checks. Click one to jump to the line
Evaluation settings
- Host-to-IP table: replaces DNS for
dnsResolve, isInNet and isResolvable. One entry per line, as "host IP" or in hosts-file order "IP host". *.example.com 10.0.0.1 applies to every host under that domain
- IP address returned by myIpAddress(): test a PAC that picks a proxy per site by entering that site's client address
- Date and time: the date, time and UTC offset used by
weekdayRange, dateRange and timeRange. Untick "Use the current date and time" to test any moment you like
- Strip the path and query from https URLs: matches Chrome and Edge (on by default)
Diff mode
Use diff mode when you change a PAC, to confirm that only the URLs you meant to change get a different result.
How diff mode works
- Press Diff mode. The PAC box splits into Old PAC (current) and New PAC (changed). The new box starts with a copy of the old one
- Put the PAC in use now in the old box and the changed PAC in the new box (Open and drag and drop work for each)
- Both are evaluated against the same URLs. Only rows whose result changed are coloured and marked Changed. The number of changes appears above the results, and ticking Changed rows only hides the other rows
Differences in whitespace around ; in the return value don't count as changes. Errors and timeouts do. The CSV includes both old and new results.
Helper functions and their pitfalls
The tester provides the 14 standard functions available inside a PAC file.
All 14 functions and their pitfalls
Their behaviour follows the Firefox and Chromium implementations (both descend from Netscape's original code).
| Function | What it does | Pitfalls |
isPlainHostName(host) | True if the host name has no dot (e.g. intranet) | False for IPv6 addresses (containing :) |
dnsDomainIs(host, domain) | True if host ends with domain | Case-sensitive. Without the leading dot, "example.com" also matches badexample.com |
localHostOrDomainIs(host, hostdom) | True if they are identical, or if a host with no domain matches the start of hostdom | localHostOrDomainIs("www", "www.example.com") is true |
isResolvable(host) | True if the host resolves to an IP address | False for hosts not in the host-to-IP table |
isInNet(host, pattern, mask) | True if the IP address is in that network | A host name is resolved through the table first. The mask must be dotted ("255.255.0.0"); /16 does not work |
dnsResolve(host) | Returns the IP address | null if it can't be resolved |
convert_addr(ip) | Converts an IP address to a 32-bit integer | Signed, as in browsers (192.168.0.1 is negative) |
myIpAddress() | The client's own IP address | Returns the value from the settings |
dnsDomainLevels(host) | The number of dots | www.example.com gives 2 |
shExpMatch(str, pattern) | Wildcard match | * is zero or more characters, ? exactly one. True only if the whole string matches. Case-sensitive. Other characters are literal here, but some browsers treat [ ] or + as regular expression syntax, so avoid them |
weekdayRange(wd1, wd2, "GMT") | Day-of-week range | Three upper-case English letters such as "MON". Can wrap around the week ("FRI", "MON"). A final "GMT" uses the UTC day |
dateRange(…) | Range of days, months and years | If the range ends with a month only, it runs to the last day of that month (browser implementations can be a few days off in months with fewer than 31 days) |
timeRange(…) | Time-of-day range | timeRange(9, 17) means 9:00 to 17:59. Hours-only ranges do not wrap past midnight (timeRange(22, 6) is always false); timeRange(22, 0, 6, 0) does |
alert(message) | Outputs a message | Shown on that URL's row in the results |
You can't use eval or new Function inside the PAC (blocked for safety). Calling new Date() directly gives your computer's clock, not the date and time in the settings.
Common mistakes (what the checks find)
The checks are approximate; read them together with the results table.
Each mistake, with examples
- Full-width spaces and punctuation: after typing comments in Japanese, Chinese or Korean, a full-width space or bracket can slip into the code. JavaScript accepts a full-width space as whitespace, so browsers won't complain. Inside comments these are fine, inside strings they are a warning, in code they are an error
- Unbalanced brackets and quotes: mismatched
{ }, ( ), [ ] and unclosed quotes, with line numbers. Parser errors are also reported
- Missing final return: a URL that matches none of the
if statements reaches the end of the function. The result is undefined, which browsers handle differently. End with return "DIRECT"; or similar
- Broken shExpMatch patterns:
shExpMatch(host, ".example.com") (missing *, matches nothing), shExpMatch(host, "http://…") (host has no scheme), shExpMatch(url, "http://www.example.com") (missing the trailing / of the URL), patterns with capital letters and so on. All patterns are also listed
- Shadowed rules: if
shExpMatch(host, "*.example.com") returns first, a later shExpMatch(host, "www.example.com") can never be reached. For simple sequences of if (shExpMatch(…)) return …; (including ||, dnsDomainIs and host == "…"), the checks report when an earlier pattern covers all of a later one. Same return value: info; different: warning
- Malformed return values:
"PROXY proxy.example.net:8080, DIRECT" (the separator is ;), a bare "PROXY", a port out of range
- Helper misuse: the
isInNet mask format, weekdayRange("mon") (lower case), timeRange(22, 6) and so on
FAQ
- Can the results differ from a real browser?
- Yes. The tester does no DNS lookups: only hosts in the host-to-IP table resolve. Browsers and operating systems also differ slightly in how they run PAC files. Do the final check in the browser and OS you use.
- How do isInNet and dnsResolve find IP addresses?
- A web page cannot query DNS, so they use the host-to-IP table in the evaluation settings. Hosts not in the table cannot be resolved: dnsResolve returns null, and isResolvable and isInNet return false.
- Are isInNetEx and the other Microsoft extensions, or IPv6, supported?
- No. Calling isResolvableEx, dnsResolveEx, myIpAddressEx, isInNetEx, sortIpAddressList or the other extension functions raises an error. The host table and myIpAddress handle IPv4 addresses only.
Notes and data handling
- What is saved: the evaluation settings are saved in your browser (localStorage key
pac-tester_settings). The PAC and test URLs are saved in pac-tester_draft only while the save option is ticked, and are deleted when you untick it. Nothing is sent anywhere
- Files: PAC files and URL lists you open are read inside the browser and not sent anywhere. CSV and URL lists are saved as downloads
Safety, licenses and the sample
- No network access: everything, including the parser library (acorn), is in one file, and it never connects to a server. The page's Content-Security-Policy (
connect-src 'none' and more) also blocks loading from and sending to other sites
- Sandboxed execution: a PAC is a program, and one you received from someone else could contain harmful code. So the PAC doesn't run in the page. It runs in a Worker started inside an iframe with the
sandbox attribute (a different origin from the page). It can't reach the page, its storage (localStorage and so on) or other tools on this site. The same network restrictions apply to the iframe and the Worker, so the PAC can't send data out
- Stoppable: if one URL takes longer than 0.2 seconds, the Worker is terminated and recreated. An infinite loop won't freeze the page
- Not saved by default: the PAC and test URLs are saved in the browser only if you tick the option to save them
- No ads or analytics in pac-tester-en.html (ads appear only on this guide and the download page)
The tool's code is under the MIT License (GitHub). pac-tester-en.html embeds this library unchanged:
| Library | Version | License | Used for |
| acorn | 8.18.0 | MIT | Parsing the PAC for the checks (it builds a syntax tree only; it doesn't run the PAC) |
The full license text is in THIRD_PARTY_LICENSES.txt and behind the License button in pac-tester-en.html.
Sample: the sample PAC is a generic example that uses only example domains (example.com, example.net, example.org, .example) and private or documentation IP addresses.
Feedback and bug reports: the page is in Japanese, but you can write in English.
The site-wide about page and disclaimer and privacy policy apply to all tools. The change log is in the Japanese guide.
Query Active Directory without RSAT from PowerShell, by the same author → ADSearch
This is an English translation of the Japanese page. If the two differ, the Japanese version prevails.