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
// 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";
}
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
// 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";
}
== 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
// 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";
}
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
// 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";
}
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
// 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";
}
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
// 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";
}
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
Zscaler Internet Access
${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";
}
Netskope explicit proxy
// 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";
}
Skyhigh Secure Web Gateway
// 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";
}
Cloudflare Gateway proxy endpoint
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";
}
Prisma Access Explicit Proxy
// 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";
}
The next step after copying a file
- Read the annotated reference proxy.pac to see why each block is where it is.
- Build a file with your own proxies and exceptions in the PAC file generator. It writes a comment next to every entry.
- Learn to write one yourself in how to create a PAC file.
- Check the 20 most dangerous PAC mistakes before you change a file by hand.
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.