PAC-K016 · zscaler-literal-gateway-address

Zscaler gateway named directly instead of through ${GATEWAY}

low · Compatibility across engines

Zscaler discourages hard-coded gateway addresses because they can change. The ${GATEWAY} variables are resolved per request by the PAC server to the closest healthy Public Service Edge; a literal gateway.<cloud>.net host or IP is static and skips that logic.

Why it matters

Zscaler writes that IP addresses “are generally discouraged because they can change at any time” and recommends ${GATEWAY} and ${SECONDARY_GATEWAY} instead, including the subcloud and _HOST forms. A literal Zscaler host name such as gateway.zscaler.net works, but it is answered by anycast or DNS-based selection rather than by the per-client geolocation of the PAC server, and it cannot take part in the _F0-_F7 / _FX load distribution. There is one documented exception: Zscaler Client Connector lets you pin traffic for specific internal hosts to a specific data centre by IP address, with at most two such return statements. The variables only expand when the PAC is hosted on the Zscaler cloud, so a self-hosted file has to use host names; in that case this is informational.

How to fix

Host the PAC on the Zscaler cloud and return "PROXY ${GATEWAY}:80; PROXY ${SECONDARY_GATEWAY}:80" (or the _HOST / subcloud forms); keep literal addresses only for the documented pin-to-data-centre case.

Examples

Bad

function FindProxyForURL(url, host) {
  if (isPlainHostName(host)) {
    return "DIRECT";
  }
  return "PROXY gateway.zscaler.net:80; PROXY gateway.zscalertwo.net:80";
}
Open bad example in checker

Good

function FindProxyForURL(url, host) {
  if (isPlainHostName(host)) {
    return "DIRECT";
  }
  return "PROXY ${GATEWAY}:80; PROXY ${SECONDARY_GATEWAY}:80";
}
Open good example in checker

Zscaler pack only.

Related rules

References