# DNS in PAC files: why isInNet and dnsResolve slow every request

Source: https://findproxyforurl.net/articles/dns-in-pac-files/ · updated 2026-10-06

The most common performance problem in PAC files is a line that looks like simple arithmetic, isInNet(host, "10.0.0.0", "255.0.0.0"). When host is a name, this is a DNS lookup, and it runs before every connection.

## What happens

`FindProxyForURL` runs before every connection. The helper functions `dnsResolve`, `isResolvable`
and `isInNet` with a host name each ask the resolver and wait for the answer
([PAC-P001](https://findproxyforurl.net/rules/dns-function-on-hot-path/)):

- normally that is tens of milliseconds added to every first byte;
- on a degraded resolver it is seconds;
- on a broken one, the request hangs.

`isInNet(host, …)` hides the lookup. In Chromium's helper library it calls `dnsResolve` whenever its
first argument is not already an IP address (source: code, verified 2026-10-04;
[PAC-P002](https://findproxyforurl.net/rules/isinnet-on-hostname/)).

The engines wait in different ways (source: code, verified 2026-10-04, unless marked):

| Engine | DNS in the PAC |
|---|---|
| Chromium | asynchronous; the script may run again once the answer arrives (expert, unverified) |
| Firefox | synchronous, blocks the PAC thread |
| pacparser | synchronous, blocking `getaddrinfo` |

## Why it also fails open

When resolution fails, `dnsResolve` returns an empty value and `isInNet` returns `false`. The rule
simply does not match, and the request takes whatever comes next, normally the default route
([PAC-X010](https://findproxyforurl.net/rules/dnsresolve-compared-to-literal/)). On a hostile network the resolver answer is
controlled by the attacker, so the routing decision is too. If a name has several addresses, only
one of them is compared.

## The fix: decide on the string first

Most PAC files can decide everything from the host name. Reorder so that cheap string tests come
first ([PAC-P005](https://findproxyforurl.net/rules/expensive-before-cheap/)), and use address tests only for IP literals:

```js
function FindProxyForURL(url, host) {
  host = host.toLowerCase();
  // 1. names: string tests, no DNS
  if (isPlainHostName(host) || dnsDomainIs(host, ".corp.example") || host == "corp.example") {
    return "DIRECT";
  }
  // 2. IP literals only: isInNet on a literal does not resolve anything
  if (/^\d+\.\d+\.\d+\.\d+$/.test(host)) {
    if (isInNet(host, "10.0.0.0", "255.0.0.0") ||
        isInNet(host, "172.16.0.0", "255.240.0.0") ||
        isInNet(host, "192.168.0.0", "255.255.0.0")) {
      return "DIRECT";
    }
  }
  // default route
  return "PROXY proxy1.corp.example:8080; PROXY proxy2.corp.example:8080";
}
```

If you really must route by the resolved address, resolve once, keep the result in a variable, and
do it after all string rules ([PAC-P004](https://findproxyforurl.net/rules/dnsresolve-not-hoisted/)):

```js
var ip = dnsResolve(host);
if (ip && isInNet(ip, "10.0.0.0", "255.0.0.0")) {
  return "DIRECT";
}
```

## Related mistakes

- `isInNet("10.0.0.0", host, "255.0.0.0")`: arguments in the wrong order, always false
  ([PAC-C016](https://findproxyforurl.net/rules/isinnet-parameter-order/)).
- An invalid network or mask such as `10.0.0` or `255:255.255.0`: always false
  ([PAC-E010](https://findproxyforurl.net/rules/isinnet-invalid-arguments/)).
- IPv6 networks in `isInNet` never match. `isInNetEx` exists only in some engines
  ([PAC-K014](https://findproxyforurl.net/rules/isinnet-with-ipv6/)).
- `isResolvable(host)` as an "is it internal" test: it answers a different question
  ([PAC-P003](https://findproxyforurl.net/rules/isresolvable-usage/)).

## Check your file

The [online PAC file tester](https://findproxyforurl.net/check/) flags every DNS call on the request path and its position in
the file. Set **DNS answers** in the tester to see how each rule behaves when a name resolves, and
when it does not.


## Frequently asked questions

### Does isInNet do a DNS lookup?

Yes, when the first argument is a host name. isInNet resolves it first and then compares the address with the network and mask. Given an IP address it does no lookup.

### Why is my PAC file slow?

Usually because it calls dnsResolve, isResolvable or isInNet on a host name for every request. Each call waits for the DNS resolver before the browser can open the connection. Decide on the host name string first and run DNS rules only for the few requests that need them, or not at all.

### What happens when DNS fails inside a PAC file?

dnsResolve returns an empty value and isInNet returns false, so the request skips the rule and takes the next one, usually the default route. Routing then depends on the resolver, which fails open.

