PAC file examples you can copy

Each file below is a complete proxy.pac that works as it stands. Copy the one that is closest to your network, replace the example host names with your own, and test it before you roll it out.

Every example starts with a short note on when to use it, then shows the file, then explains what to watch out for. All files are written in ES5, the JavaScript version that every PAC engine understands, and none of them makes the browser wait for a DNS lookup. Each one scores A+ in the PAC file checker.

The host names and networks are reserved example names: corp.example stands for your internal zone, proxy.corp.example:8080 for your proxy, and 198.51.100.0/24 for an address range of your own. Replace them before you use a file.

Send everything through one proxy

This is the smallest PAC file that works. Use it when every destination, including internal servers, should go through the same proxy, for example on a guest network or in a lab.
// proxy.pac example: one proxy for everything
// Synthetic example from https://findproxyforurl.net/examples/ - replace the proxy name and port.
function FindProxyForURL(url, host) {
  // Default route: every request goes through the corporate proxy
  return "PROXY proxy.corp.example:8080";
}

Test this file

The return at the end is the default route: the answer for every request that no earlier test matched. Here it is the only statement. With a single proxy and nothing else in the list, browsers have nowhere to go when that proxy is down, so either point it at a highly available address or use the failover example below.

Keep local traffic direct and send the rest to the proxy

This is the most common PAC file in companies. Internal servers are reached directly, and everything on the internet goes through the proxy. Start here if you are not sure which example fits.
// proxy.pac example: local destinations direct, everything else through the proxy
// Synthetic example from https://findproxyforurl.net/examples/ - replace the names and networks.
function FindProxyForURL(url, host) {
  // host names are case-insensitive, string comparisons are not
  host = host.toLowerCase();

  // plain host names (no dot) are on the local network
  if (isPlainHostName(host)) {
    return "DIRECT";
  }

  // the internal DNS zone and all its hosts
  if (host == "corp.example" || dnsDomainIs(host, ".corp.example")) {
    return "DIRECT";
  }

  // IP-address URLs in private and loopback ranges; names are never resolved here
  if (/^\d+\.\d+\.\d+\.\d+$/.test(host) &&
      (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") ||
       isInNet(host, "127.0.0.0", "255.0.0.0"))) {
    return "DIRECT";
  }

  // Default route: everything else goes through the corporate proxy
  return "PROXY proxy.corp.example:8080";
}

Test this file

The file lower-cases the host first, because the comparisons with == are case-sensitive. Plain host names are names without a dot, such as intranet; isPlainHostName() is the cheapest test, so it comes first. An internal zone needs two tests: host == "corp.example" for the zone name itself and dnsDomainIs(host, ".corp.example") for every host in it. Private address ranges are tested only when the URL contains an IP address, because isInNet() on a name makes the browser wait for a DNS lookup (why that hurts).

Use a different proxy for some domains

Use it when some destinations need another route than the default: a partner network behind its own gateway, a mirror in the DMZ that is reached directly, or a lab with its own proxy.
// proxy.pac example: a different route per destination domain
// Synthetic example from https://findproxyforurl.net/examples/ - replace the names.
function FindProxyForURL(url, host) {
  // host names are case-insensitive, string comparisons are not
  host = host.toLowerCase();

  // plain host names and the internal zone stay on the local network
  if (isPlainHostName(host) || host == "corp.example" || dnsDomainIs(host, ".corp.example")) {
    return "DIRECT";
  }

  // partner systems are reached through the proxy at the partner VPN gateway
  if (host == "partner.example" || dnsDomainIs(host, ".partner.example")) {
    return "PROXY partner-gw.corp.example:3128";
  }

  // the software update mirror is in the DMZ and is reached without a proxy
  if (host == "updates.example.net") {
    return "DIRECT";
  }

  // the development lab has its own proxy, with the main proxy as fallback
  if (host == "lab.example" || dnsDomainIs(host, ".lab.example")) {
    return "PROXY labproxy.corp.example:8080; PROXY proxy.corp.example:8080";
  }

  // Default route: everything else goes through the corporate proxy
  return "PROXY proxy.corp.example:8080";
}

Test this file

Each if block handles one destination group and ends in a return, so the first matching block decides. Put the groups that match most requests first. Every entry that is a whole domain uses the pair host == "x" and dnsDomainIs(host, ".x"); an entry for one host uses == alone.

Fail over to a second proxy when the first one does not answer

Use it whenever you have two proxies, for example one per data centre. The browser tries the entries of the returned list from left to right and takes the next one when a proxy does not answer.
// proxy.pac example: two proxies, the second one takes over when the first does not answer
// Synthetic example from https://findproxyforurl.net/examples/ - replace the names.
function FindProxyForURL(url, host) {
  // host names are case-insensitive, string comparisons are not
  host = host.toLowerCase();

  // plain host names and the internal zone stay on the local network
  if (isPlainHostName(host) || host == "corp.example" || dnsDomainIs(host, ".corp.example")) {
    return "DIRECT";
  }

  // Default route, in failover order:
  //   1. proxy1 in data centre 1 (primary)
  //   2. proxy2 in data centre 2 (used when proxy1 does not answer)
  // No DIRECT at the end: when both proxies are down, requests fail instead of bypassing the proxy.
  return "PROXY proxy1.corp.example:8080; PROXY proxy2.corp.example:8080";
}

Test this file

How long a browser skips a failed proxy before it tries it again differs between clients. There is deliberately no DIRECT at the end of the list: with it, all traffic would bypass the proxy as soon as both proxies are unreachable. If that is what you want, add it on purpose (the trade-off).

Reach one zone through a SOCKS proxy

Use it when part of the network is reachable only through a gateway that offers SOCKS, for example an isolated lab, while normal web traffic uses HTTP proxies.
// proxy.pac example: one zone through a SOCKS proxy, everything else through an HTTP proxy
// Synthetic example from https://findproxyforurl.net/examples/ - replace the names.
function FindProxyForURL(url, host) {
  // host names are case-insensitive, string comparisons are not
  host = host.toLowerCase();

  // plain host names and the internal zone stay on the local network
  if (isPlainHostName(host) || host == "corp.example" || dnsDomainIs(host, ".corp.example")) {
    return "DIRECT";
  }

  // the isolated lab network is reachable only through its gateway, which offers an HTTP proxy
  // and SOCKS. HTTP first: Windows misreads SOCKS5 and .NET programs ignore SOCKS entries.
  // Plain SOCKS as the fallback, the one SOCKS keyword that every engine reads.
  if (host == "lab.example" || dnsDomainIs(host, ".lab.example")) {
    return "PROXY labgw.corp.example:3128; SOCKS labgw.corp.example:1080";
  }

  // Default route: everything else goes through the HTTP proxies, in failover order
  return "PROXY proxy1.corp.example:8080; PROXY proxy2.corp.example:8080";
}

Test this file

The PAC format has only one SOCKS keyword and does not say which version of the SOCKS protocol it means. Chromium treats plain SOCKS as version 4 and Firefox is reported to use version 5. In the lab on Windows 11 (2026-10-11) Windows misread the explicit keywords SOCKS5 and SOCKS4, and .NET programs ignored SOCKS entries altogether. The file therefore sends the zone to the gateway’s HTTP proxy first and keeps plain SOCKS only as the fallback. Test the file on every client you support (details).

Give each site its own proxy with one file per site

Use it when branch offices have their own proxies and each client should use the proxy of the site it is in, with the head-office proxy as fallback.
// proxy.pac example: the file for one branch office (here the Berlin site)
// Every site gets its own copy with its own proxy first; the site's DHCP scope or GPO hands out its URL.
// Synthetic example from https://findproxyforurl.net/examples/ - replace the names.
function FindProxyForURL(url, host) {
  // host names are case-insensitive, string comparisons are not
  host = host.toLowerCase();

  // plain host names and the internal zone stay on the local network
  if (isPlainHostName(host) || host == "corp.example" || dnsDomainIs(host, ".corp.example")) {
    return "DIRECT";
  }

  // Default route for this site, in failover order:
  //   1. the Berlin branch proxy
  //   2. the head-office proxy, reached over the WAN, when the branch proxy does not answer
  return "PROXY proxy.ber.corp.example:8080; PROXY proxy.hq.corp.example:8080";
}

Test this file

The obvious way to do this in one file is to test the client’s address with myIpAddress(). That is unreliable: a laptop has several addresses (Wi-Fi, Ethernet, VPN, virtual adapters), each browser picks “its” address in a different way, and the answer does not change when the VPN connects until the browser runs the file again (details). The reliable way is one small file per site, served under a different URL, which the site’s DHCP scope or a group policy for the site’s computers hands out. This example is the file for the Berlin site; the other sites get a copy with their own proxy first.

Files for security service edge (SSE) vendors

A security service edge (SSE) is a cloud service that inspects web traffic, such as Zscaler, Netskope, Skyhigh Security, Cloudflare Gateway or Prisma Access. These files are the default output of the site’s SSE builders, which follow each vendor’s documentation. The “Test this file” link opens the checker with the vendor’s rule pack selected, so the vendor-specific checks run too.

Zscaler Internet Access

A hosted PAC file for Zscaler Internet Access. The ${GATEWAY} and ${SECONDARY_GATEWAY} variables are replaced by the Zscaler cloud when it serves the file, so the file must be hosted there.
// proxy.pac for Zscaler Internet Access, generated by findproxyforurl.net
// Host this file on the Zscaler cloud so that the ${...} variables are replaced.
function FindProxyForURL(url, host) {
  host = host.toLowerCase();

  // plain hostnames (no dot) stay on the local network
  if (isPlainHostName(host)) {
    return "DIRECT";
  }

  // RFC 1918 and loopback addresses: literal IPs only, no DNS lookup for names
  if (/^\d+\.\d+\.\d+\.\d+$/.test(host) &&
      (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") ||
       isInNet(host, "127.0.0.0", "255.0.0.0"))) {
    return "DIRECT";
  }

  // internal domains and their subdomains go direct
  if (dnsDomainIs(host, ".corp.example")) {
    return "DIRECT";
  }

  // the Zscaler service does not support native FTP
  if (url.substring(0, 4) == "ftp:") {
    return "DIRECT";
  }

  // default: everything else goes to the closest Public Service Edge, then to the secondary one
  return "PROXY ${GATEWAY}:80; PROXY ${SECONDARY_GATEWAY}:80";
}

Test this file Build your own in the Zscaler Internet Access builder

Netskope explicit proxy

A PAC file for the Netskope Cloud Explicit Proxy, which listens on port 8081. The identity provider’s sign-in hosts go direct.
// proxy.pac for Netskope explicit proxy, generated by findproxyforurl.net
function FindProxyForURL(url, host) {
  host = host.toLowerCase();

  // plain hostnames (no dot) stay on the local network
  if (isPlainHostName(host)) {
    return "DIRECT";
  }

  // RFC 1918 and loopback addresses: literal IPs only, no DNS lookup for names
  if (/^\d+\.\d+\.\d+\.\d+$/.test(host) &&
      (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") ||
       isInNet(host, "127.0.0.0", "255.0.0.0"))) {
    return "DIRECT";
  }

  // internal domains and their subdomains go direct
  if (dnsDomainIs(host, ".corp.example")) {
    return "DIRECT";
  }

  // identity provider (Microsoft Entra ID) must not go through the explicit proxy
  if (host == "login.microsoftonline.com" || host == "aadcdn.msauth.net" || host == "aadcdn.msftauth.net") {
    return "DIRECT";
  }

  // the explicit proxy accepts HTTP and HTTPS only
  if (url.substring(0, 4) == "ftp:") {
    return "DIRECT";
  }

  // default: everything else goes to the Netskope Cloud Explicit Proxy (port 8081 is required)
  return "PROXY eproxy-exampletenant.goskope.com:8081";
}

Test this file Build your own in the Netskope explicit proxy builder

Skyhigh Secure Web Gateway

A PAC file for the Skyhigh cloud proxy. Replace the customer ID in the host name with your own.
// proxy.pac for Skyhigh Secure Web Gateway, generated by findproxyforurl.net
function FindProxyForURL(url, host) {
  host = host.toLowerCase();

  // plain hostnames (no dot) stay on the local network
  if (isPlainHostName(host)) {
    return "DIRECT";
  }

  // RFC 1918 and loopback addresses: literal IPs only, no DNS lookup for names
  if (/^\d+\.\d+\.\d+\.\d+$/.test(host) &&
      (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") ||
       isInNet(host, "127.0.0.0", "255.0.0.0"))) {
    return "DIRECT";
  }

  // internal domains and their subdomains go direct
  if (dnsDomainIs(host, ".corp.example")) {
    return "DIRECT";
  }

  // default: everything else goes to the Skyhigh cloud proxy (port 80 or 8080)
  return "PROXY c1234567890.wgcs.skyhigh.cloud:8080";
}

Test this file Build your own in the Skyhigh Secure Web Gateway builder

Cloudflare Gateway proxy endpoint

A PAC file for a Cloudflare Gateway proxy endpoint. The endpoint expects TLS to the proxy, which is why the file uses the HTTPS keyword on port 443.
// proxy.pac for a Cloudflare Gateway proxy endpoint, generated by findproxyforurl.net
function FindProxyForURL(url, host) {
  host = host.toLowerCase();

  // identity provider (Microsoft Entra ID) is bypassed to prevent authentication loops
  if (host == "login.microsoftonline.com" || host == "aadcdn.msauth.net" || host == "aadcdn.msftauth.net") {
    return "DIRECT";
  }

  // plain hostnames (no dot) stay on the local network
  if (isPlainHostName(host)) {
    return "DIRECT";
  }

  // RFC 1918 and loopback addresses: literal IPs only, no DNS lookup for names
  if (/^\d+\.\d+\.\d+\.\d+$/.test(host) &&
      (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") ||
       isInNet(host, "127.0.0.0", "255.0.0.0"))) {
    return "DIRECT";
  }

  // internal domains and their subdomains go direct
  if (dnsDomainIs(host, ".corp.example")) {
    return "DIRECT";
  }

  // default: everything else goes to the Gateway proxy endpoint (TLS to the proxy, hence HTTPS)
  return "HTTPS abc123def0.proxy.cloudflare-gateway.com:443";
}

Test this file Build your own in the Cloudflare Gateway proxy endpoint builder

Prisma Access Explicit Proxy

A PAC file for Prisma Access Explicit Proxy on port 8080. The identity provider and the Authentication Cache Service go direct, because SAML sign-in loops otherwise.
// proxy.pac for Prisma Access Explicit Proxy, generated by findproxyforurl.net
function FindProxyForURL(url, host) {
  host = host.toLowerCase();

  // plain hostnames (no dot) stay on the local network
  if (isPlainHostName(host)) {
    return "DIRECT";
  }

  // RFC 1918 and loopback addresses: literal IPs only, no DNS lookup for names
  if (/^\d+\.\d+\.\d+\.\d+$/.test(host) &&
      (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") ||
       isInNet(host, "127.0.0.0", "255.0.0.0"))) {
    return "DIRECT";
  }

  // internal domains and their subdomains go direct
  if (dnsDomainIs(host, ".corp.example")) {
    return "DIRECT";
  }

  // Explicit Proxy supports HTTP and HTTPS only
  if (url.substring(0, 4) == "ftp:") {
    return "DIRECT";
  }

  // SAML identity provider (Okta) must go direct
  if (dnsDomainIs(host, ".okta.com") || dnsDomainIs(host, ".oktacdn.com")) {
    return "DIRECT";
  }

  // Authentication Cache Service must go direct, otherwise SAML authentication loops
  if (shExpMatch(host, "*.acs.prismaaccess.com")) {
    return "DIRECT";
  }

  // default: everything else goes to Prisma Access Explicit Proxy (port 8080)
  return "PROXY example.proxy.prismaaccess.com:8080";
}

Test this file Build your own in the Prisma Access Explicit Proxy builder

The next step after copying a file

Frequently asked questions

Where can I find a simple proxy.pac example?

The first example on this page is the smallest PAC file that works. It sends every request to one proxy. The second one is the most common company file. It keeps internal names and private addresses direct and sends everything else to the proxy.

Can I use these PAC files as a template?

Yes. All host names and addresses in them are reserved example names (corp.example, example.net, 198.51.100.0/24), so you must replace them with your own. Then test the result in the PAC file checker with the URLs that matter to you.

What is the difference between these examples and the generator?

The examples are fixed files that show one pattern each. The generator builds a file from your own lists of proxies, hosts, domains and networks, and writes a comment next to every entry.

Why do the examples not use myIpAddress() or dnsResolve()?

Both make routing depend on things the PAC file cannot control. dnsResolve() makes the browser wait for a DNS lookup, and myIpAddress() returns a different address depending on the browser and the active network adapter. The examples avoid both, which is also why they score A+.

Do these files work as wpad.dat?

Yes. A wpad.dat is a PAC file under another name. Serve the same content as wpad.dat if your clients find it through WPAD auto-discovery.