The Only PAC File you will need v1.1

Hi All,

I prepared a PAC file with the help of Zscaler that will include most of the scenarios we face.
The comments and extra code added are to help you create your own PAC file.

Make sure to remove any comments from the PAC file to minimize the file size.

Do let me know if you want to update the PAC file or have any suggestions.

function FindProxyForURL(url, host) {

    //        ****************************************************************************
    //        This is an example PAC file that should be edited before being put to use.
    //        ****************************************************************************

    //        Consider the following:
    //         - Keep production PAC files small. Delete all comments if possible
    //         - Delete any examples or sections that do not fit your needs
    //         - Consolidate bypass criteria into fewer if() statements if possible
    //         - Be sure you are bypassing only traffic that *must* be bypassed
    //         - Be sure not to perform any DNS resolution in the PAC
    //         - Zscaler recommends sending bypassed internet traffic via on-premise proxy compared
    //           to the internet directly
    //         - Recommended PAC file size 25KB for best user experience

    //==============================Section I ==== Internal/Specific Destinations ============================== 

    //        Most special use IPv4 addresses (RFC 5735) are defined within this regex.
    var privateIP = /^(0|10|127|192\.168|172\.1[6789]|172\.2[0-9]|172\.3[01]|169\.254|192\.88\.99)\.[0-9.]+$/;
    var resolved_ip = dnsResolve(host);

    // If the url is https and the URL is an IP return direct, this can be useful if you want to bypass remote device that use a Public IP
     /*   var get_url = url.substring(8, 21);
    var ip_checker = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/;
    if (ip_checker.test(get_url) && url.substring(0, 6) == "https:") {
        return "DIRECT";
    //Bypass proxy for internal hosts
    // This is not recommended to use as it is bypassed by default from ZCC app profile, in case you are using the Zscaler Client Connector
    if (
        isInNet(host, "", "") ||
        isInNet(host, "", "") ||
        isInNet(host, "", "") ||
        isInNet(host, "", "") ||
        isInNet(host, "", "") ||
        isInNet(host, "", "")
    ) {
        return "DIRECT";

    //Specific destinations can be bypassed here. Example lines for the host, and
    //domain provided. Replace with your specific information. 
    //Add internal domains that cannot be publicly resolved here.

    if (isPlainHostName(host) || (host == "") ||
        shExpMatch(host, "*"))
        return "DIRECT";

    //============================== Section II ==== Website/URL Bypass or Block ============================== 
//######IMPORTANT NOTE: bypassed Traffic should be assessed for risk. There should be no reason to bypass traffic from ZTE=====

    if (shExpMatch(host, "*") || shExpMatch(host, "*"))
        return "DIRECT";

    // you can use the PAC file to send the traffic to a black hole, in some cases, can be useful if you want to utilise it for blocking
    if (shExpMatch(host, "**")
        return "PROXY proxy.testDomain.local:8080";// you can keep "" or replace it with any dead end destination

    // ============================== Section III ==== Bypasses Using DNS resolution ============================
//######IMPORTANT NOTE: bypassed Traffic should be assessed for risk. There should be no reason to bypass traffic from ZTE=====

    // If you have a website that is hosted both internally and externally,
    //If you want to bypass the proxy for the internal version only, use the following

    //Be sure to optimize DNS resolution. Only perform DNS resolution on the client if necessary. 
    //DNS resolution in the PAC file can cause performance issues while waiting for the DNS response and is then perceived as Browser/PAC/Proxy issue.

    /* Don't send non-FQDN*/
    if (shExpMatch(host("*.internal.domain"))) {
        if (privateIP.test(resolved_ip))
            return "DIRECT";

    // you can use dnsDomainIs to match the domain of the hostname
    //not recommended to use it a lot as it could affect the performance
    if (
        (dnsDomainIs(host, ""))
    ) {
        return "DIRECT";

    // ============================== Section IV ==== Bypasses for Zscaler ===================================

    //        Go direct for queries about Zscaler infrastructure status 

    var trust = /^(trust|ips).(zscaler|zscalerone|zscalertwo|zscalerthree|zsdemo|zscalergov|zscloud|zsfalcon|zdxcloud|zdxpreview|zdxbeta|zspreview|zsdevel|zsbetagov|zscalerten|zdxten|zdxdevel|zdxgov|zsqa).(com|net)$/;
    if (trust.test(host) || /^$/.test(host))
        return "DIRECT";

    // ============================== Section V ==== Bypasses for ZPA ===================================
    /* test with ZPA*/
    if (isInNet(resolved_ip, "", ""))
        return "DIRECT";

    // ============================== Section VI ==== DEFAULT FORWARDING ================================ 

    // Forward Traffic for sepcific websites that are Geolocation locked
    // List of datacenter can be retrieved from ""
    // Make sure to pick the currect ZEN based on your cloud e.g,
    if (shExpMatch(host, "*"))
        return "PROXY";

    // list of supported country variables is in ""
    //example: if the user is in the US, always send traffic to ZEN NYC3
    var country = "${COUNTRY}";
    if (shExpMatch(country, "United States")) {
        return "PROXY";

    //        If your company has purchased a dedicated port, kindly use that in this file.
    //        Port 9400 is the default port, followed by 80. If that does not resolve, we send directly:
    //For Dynamic ZEN

    //if you have a subcloud
    //return "PROXY ${} 9400; ${}:9400; ${}:80; PROXY ${}:80; DIRECT";

I updated the PAC file based on your comments thanks


I’d be really curious to know if your blackhole section works as expected and under what conditions. I tried this about a year ago (mind you I am ZCC-only, no tunnels or PAC-only users), and it didn’t work for me trying to blackhole I was trying to achieve a similar effect to using a Pi-hole DNS blocklist or hosts file.

I also really like the cleaned way of checking for Zscaler entries with RegEx.

1 Like


I would rather use the exclusions in the app profile if ZCC is in place.
We experienced slow downs in web browsing when using IsInNet or something similiar …

Regards Thomas

1 Like

isInNet always triggers DNS requests - and this typically adds latency nobody wants ot needs… :wink:

better replace that with this (handles all private IP ranges and pretty much all other ‘special’ IP ranges which shouldn’t show up in the internet):

// check for (majority of) RFC5735/6598 IPs
// get the IP host resolves to

/* Don't send non-FQDN or private IP stuff to proxy */
if (isPlainHostName(host) || privateIP.test(resolvedIP)) return "DIRECT";
1 Like

The Point of this PAC file is to explore the possibilities you can do with javascript.

I will add a comment to that section of the code.


In that case we no longer need that section in the PAC file since it is already done from the ZCC app Profile.

The blackhole section did work for us.

I already used it to simulate a DR test for Outlook where we sent the traffic to an unreachable IP.

1 Like

Thanks so much for the feedback! I will try your methodology.

One thing to keep in mind is that if Z-Tunnel 2.0, bypasses must be done using the App Profile and not the PAC files.
Also, is this a PAC file for Forwarding Profile or App Profile? They are two different distinct functions.

Hello Thomas. Just to clarify an important point, the isInNet function does not trigger DNS requests if you pass it the resolved IP as the variable. So in your example of doing the dnsResolve(host) above, that triggers a DNS request, but the variable then becomes the resolved IP, and you could use the regex .test function on that IP, or you could still use the isInNet function on that resolved IP without triggering any more DNS lookups.

1 Like

This is for the App Profile, which works with Z-Tunnel 2.0 as long as you remove the exclusions.