PAC file troubleshooting: why proxy.pac or wpad.dat does not work, and how to fix it
A PAC file that is not working rarely shows an error. Traffic just goes direct, to the wrong proxy, or slowly. Start from what you see, find the cause, and confirm the fix with a step-by-step trace of the failing URL.
A PAC file is a small JavaScript program that the browser runs for every request. The page What is a PAC file? explains it from the start. When something goes wrong, the browser rarely tells you. When the script throws an exception or returns nothing, Chromium and Firefox fall back to a direct connection (source: code, verified 2026-10-04). Chromium also goes direct when it cannot fetch the file, unless a policy marks the PAC as mandatory (source: code, verified 2026-10-04). So the first sign of trouble is often a complaint that “the proxy is not used”, or proxy logs that stay empty.
This page starts from what you see. Each symptom below says how to recognise it, what usually causes it and how to confirm the cause. For problems inside the script, the quickest way to confirm is the live PAC trace in the online PAC file tester. It runs your file in your browser for one URL and one client IP, and it shows step by step how the script reached its answer.
How to trace one URL through your PAC file
- Paste the file into the tester, or open it with Load file….
- Enter the failing domain or URL above the editor. A domain alone is tested as
https://. - If a rule uses
myIpAddress(), enter the client IP of the affected user. Otherwise the tester uses 192.168.1.100. - Choose the engine profile of the affected browser, for example the current Chromium version.
- Press Check. The editor area switches to the Path tab.
The Path tab shows your file with the path of the URL marked on it:
- a number in front of each line that ran, in the order the lines ran;
- ✔ or ✘ on every
iftest, for true or false; - under the line, every built-in call it made, with its arguments and its result;
- ▶ on the
returnthat decided, with the result; - dimmed lines for branches that were skipped.
If you have inherited the file and first want to know what it does at all, open the Annotated tab. It shows the file with an explanation comment above each rule, read from the code without running it, and you can download the annotated copy.
The trace runs in a sandbox in your browser. The file and the URL are not sent anywhere. Check also rates the whole file against the rule catalogue, so you get the list of known mistakes at the same time.
A worked example: a look-alike domain slips past the proxy
A company wants its own domain corp.example to go direct and everything else through the proxy:
function FindProxyForURL(url, host) {
host = host.toLowerCase();
if (isPlainHostName(host) || shExpMatch(host, "*corp.example")) {
return "DIRECT";
}
return "PROXY proxy.corp.example:8080";
}
A user reports that evilcorp.example is not filtered. The trace of evilcorp.example shows why:
https://evilcorp.example/ → DIRECT (line 4) · Chrome ≥ 52
1 3 ✔ if (isPlainHostName(host) || shExpMatch(host, "*corp.example")) {
↳ isPlainHostName("evilcorp.example") → false
↳ shExpMatch("evilcorp.example", "*corp.example") → true
2 4 ▶ return "DIRECT";
⇒ DIRECT
The pattern *corp.example has no dot after the *, so it also matches every domain that merely
ends in “corp.example”. The fix is dnsDomainIs(host, ".corp.example") || host === "corp.example"
(PAC-X004). After the edit, Check again: the
same URL now reaches line 6 and returns the proxy. The tab label says “(outdated)” until you check
again, so you cannot mistake an old trace for the current file.
The PAC file is not loaded, or wpad.dat is not found
What you see. Traffic goes direct, or there is no proxy at all. Windows applications that use WinHTTP report one of these errors (source: doc, Microsoft WinHTTP error messages, checked 2026-10-11):
| Error | Number | What Microsoft says |
|---|---|---|
ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT | 12167 | The PAC file cannot be downloaded, for example because the server was not reachable or answered 404 |
ERROR_WINHTTP_AUTODETECTION_FAILED | 12180 | WinHTTP could not discover the URL of the PAC file |
ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR | 12178 | A proxy for the URL cannot be located |
Chrome and Edge connect directly. Only when a policy marks the PAC as mandatory do they stop with
ERR_MANDATORY_PROXY_CONFIGURATION_FAILED (net error -131). Chromium’s source describes this error
as “a mandatory PAC script could not be fetched, parsed or executed” (source: code,
net_error_list.h,
checked 2026-10-11).
Usual causes and checks:
- The URL does not return 200. Open the PAC URL from the affected machine, with the same proxy settings, and look at the status code and any redirect (PAC-D010).
- Auto-detection finds nothing. With “Automatically detect settings”, the client looks for the
PAC URL in DHCP option 252 and by a DNS lookup of the name
wpad. Check thatwpadresolves on the client and thathttp://wpad.<your domain>/wpad.datanswers. The pages WPAD and auto-detection explain both lookups and their limits. - The file is served from a file path or an SMB share. Chromium no longer accepts
file://PAC URLs, so the setting silently does nothing there, while Windows components may still use it (PAC-D009). - The content type is wrong. Serve the file as
application/x-ns-proxy-autoconfig(PAC-D002).
To check the delivery of the file, paste the response headers into the tester under “Delivery”:
curl -sI https://pac.corp.example/proxy.pac.
The file loads, but the script does not run
What you see. As above, traffic goes direct. WinHTTP reports
ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT, error 12166: “An error occurred executing the script code in
the Proxy Auto-Configuration (PAC) file.” Chromium records ERR_PAC_SCRIPT_FAILED (-327) in the
NetLog.
Usual causes:
- A syntax error, often from a missing brace or a quote after a hand edit (PAC-E001). The tester shows “The PAC could not be loaded” with the line.
- Modern JavaScript in an older engine.
?.,??and object spread work in current browsers but stop the whole file from loading on Windows (PAC-K020);let,const, arrow functions and methods such asincludesfail in older engines such as pactester 1.4 (PAC-K001, PAC-K010). The tester flags both. - An exception at run time, for example a call to a function that does not exist in this
engine. The Microsoft
isInNetExfamily does not exist in Firefox (PAC-K003, PAC-E006). The trace stops at the line that threw, and the result line shows the error. - No return value for some URLs. Chromium and Firefox then go direct (PAC-X001).
- Invisible characters pasted from a web page or a chat, such as a byte order mark or a zero-width space (PAC-E013, PAC-E015).
A URL goes to the wrong proxy, or direct
This is the most common case, and the one the live trace is made for. Trace the failing URL and look for the first ✔ or ✘ that you did not expect. The call under that line shows the exact value the script compared. Typical findings:
- The rule tests
urlinstead ofhost. A host pattern applied to the full URL also matches the query string, for examplehttps://evil.example/?corp.example(PAC-C001). - The browser hides the path. For
https://URLs, Chrome passes onlyhttps://host:port/to the script, and Firefox does this for every scheme by default (source: code, verified 2026-10-04). A rule on a path never matches there. The trace shows the URL the engine passed under the code. - Upper case.
shExpMatchand==compare case-sensitively. Lower-casehostfirst (PAC-K009). - A pattern matches too much or too little: a leading
*without a dot, adnsDomainIspattern without a leading dot, or regular-expression syntax inshExpMatch(PAC-X003, PAC-K015, PAC-C009). - An earlier rule wins. The step numbers show which
returncame first (PAC-C014). isInNetwith swapped arguments or a wrong mask (PAC-C016, PAC-C007).- It worked in pactester. pactester and the browsers answer some calls differently, for
example
isInNetwith"10.0.0.0/8"or a range such asweekdayRange("FRI", "MON"); see pactester vs Chrome and Firefox.
The result depends on the user’s network. Rules with myIpAddress() give different answers at
home, in the office and on VPN, and on machines with several network adapters the function may
return another address than you expect (PAC-K013). Enter the
affected user’s address as the client IP and check again. The trace shows what myIpAddress()
returned and which branch that chose.
The proxy itself fails. If the trace returns the right proxy but the page still fails, the
problem is after the PAC. Chrome shows ERR_PROXY_CONNECTION_FAILED (-130) when it cannot connect
to the proxy the script returned. Firefox shows “The proxy server is refusing connections” or
“Unable to find the proxy server” (expert).
Every page is slow
What you see. Pages start late, especially on VPN or on a bad network, while the proxy itself is
fast. The usual cause is a DNS lookup in the PAC on every request: isInNet(host, …),
dnsResolve or isResolvable (PAC-P001,
PAC-P002). The trace lists every dnsResolve and isInNet call
for the URL, so you can see how many lookups one request costs. The article
DNS in PAC files shows how to write the same rules without DNS.
A change does not take effect
Browsers and Windows keep the downloaded PAC file for a while. Without a Cache-Control header,
the engine decides how long (PAC-D003). While testing, use a fresh
browser profile and restart it after each change. The article
Debugging a PAC file in Chrome, Edge and Firefox shows
how to load a PAC file into a test profile and how to read what the browser really did.
Before you open a ticket
Most “the browser ignores my PAC” reports turn out to be one of the top 20 PAC mistakes. Trace the failing URL in the tester, read the findings, and then compare with the real browser. If the trace and the browser disagree, the browser log shows why. The page best practices lists the habits that prevent most of these problems.
Frequently asked questions
Why is my PAC file not working?
There are three usual reasons. The client cannot download the file, the script fails when it runs, or the script runs but returns a different proxy than you expect. A failed download or a script error usually makes the browser go direct without any message. Paste the file into the tester at findproxyforurl.net/check/ and enter the failing URL. The live trace shows whether the file loads, which tests were true or false, and which line decided.
Why is my proxy.pac not loaded?
The client either does not know the PAC URL or cannot download the file. Open the PAC URL from the affected machine and check that it answers with status 200, without a redirect to a login page. A file:// path or an SMB share does not work in Chromium. With auto-detection, check that DHCP option 252 or the DNS name wpad points to the file. Windows reports a failed download as WinHTTP error 12167.
What are the most common PAC file issues?
A host pattern that matches look-alike domains, a rule that tests url instead of host, a missing default return, modern JavaScript that older engines reject, DNS lookups with isInNet or dnsResolve on every request, and a file that is cached too long after a change. The top 20 list at findproxyforurl.net/top-20/ ranks them, and the tester checks a file for all of them.
What does WinHTTP error 12167 mean?
ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT, error 12167, means that Windows could not download the PAC file. For example, the server in the PAC URL was not reachable, or it answered 404 Not Found. Open the PAC URL from the same machine and check the status code. Error 12166 means that the file was downloaded but its script failed, and error 12180 means that auto-detection found no PAC URL.
Why does Chrome go direct although a PAC file is set?
When Chromium cannot fetch, parse or run the PAC file, or the script throws an exception or returns nothing, it connects directly. Only when a policy marks the PAC as mandatory does it stop with ERR_MANDATORY_PROXY_CONFIGURATION_FAILED instead. Look at the NetLog from chrome://net-export to see which case applies.
How can I see which rule in my PAC file matched a URL?
Use a tester that traces the script. On findproxyforurl.net/check/, enter the domain or URL, and the client IP if a rule uses myIpAddress(). Then press Check. The Path tab shows your file with the order of the lines that ran, true or false for each test, every built-in call with its arguments and result, and the line that returned the answer.
Why is wpad.dat not found?
With auto-detection the client looks for the PAC URL in DHCP option 252 and by a DNS lookup of the name wpad in its own DNS suffix search list. If neither gives an answer, there is no PAC file. On Windows WinHTTP this ends with error 12180. Check that the name wpad resolves on the client, that the server answers for http://wpad.<domain>/wpad.dat, and that the file is served with status 200.