Home > Networking > Configuration of PAC file for dynamic proxy selection

Configuration of PAC file for dynamic proxy selection

Introduction

Deploying a Proxy Auto-Configuration (PAC) file to manage network traffic routing is a powerful way to control proxy usage in an organization. However, the process can be tricky. In this post, I’ll share my journey of deploying a PAC file, including the pitfalls of hosting it on a local drive, the eventual success by moving it to a web server, and the steps I took to ensure high availability using a load balancer. If you’re planning to deploy a PAC file, I hope my experience helps you avoid common pitfalls!

Background

Our goal was to implement a PAC file to route internal network traffic (e.g., private IP ranges and specific domains like *.company.local) directly while sending other traffic through proxy servers (xproxy.company.local:8080 and yproxy.company.local:8080). The PAC file used the standard FindProxyForURL(url, host) function to make routing decisions based on IP ranges and domain patterns. Here’s a simplified version of the PAC file:

javascript

function FindProxyForURL(url, host) {
    var primaryProxy = "PROXY xproxy.company.local:8080";
    var standbyProxy = "PROXY yproxy.company.local:8080";
    var direct = "DIRECT";

    var resolved_ip = dnsResolve(host);

    if (isInNet(resolved_ip, "10.0.0.0", "255.0.0.0") ||
        isInNet(resolved_ip, "172.16.0.0", "255.240.0.0") ||
        isInNet(resolved_ip, "192.168.0.0", "255.255.0.0") ||
        shExpMatch(host, "*.company.local")) {
        return direct;
    }

    return primaryProxy + "; " + standbyProxy + "; " + direct;
}

The challenge was deploying this PAC file reliably across our organization while ensuring performance and availability.

The Initial Failure: Hosting the PAC File on a Local Drive

Initially, I tried hosting the PAC file on a local drive or network share (e.g., file://C:/proxy.pac or file://server/share/proxy.pac). The idea was to keep things simple: place the file locally or on a shared drive, configure browsers to use it, and avoid setting up a web server. Unfortunately, this approach failed for several reasons:

  1. Browser Restrictions:
    • Modern browsers (e.g., Chrome, Firefox, Edge) restrict access to file:// URLs for security reasons, often resulting in errors or ignored settings.
    • Network shares (file://server/share/proxy.pac) were unreliable, especially for remote users or those on VPNs, as access depended on network connectivity and permissions.
  2. Performance Issues:
    • Accessing a PAC file over a network share introduced latency, particularly for users on slow or unstable connections.
    • Some browsers failed to cache the PAC file properly with file://, leading to repeated reads and slowdowns.
  3. Scalability and Maintenance:
    • Distributing the PAC file to every client’s local drive was impractical for a large organization. Updating the file required redeploying it to all clients, which was time-consuming and error-prone.
    • Network shares weren’t designed for high-frequency access by many clients, causing potential bottlenecks.
  4. Availability:
    • If the network share was down or inaccessible (e.g., due to server maintenance or VPN issues), browsers couldn’t load the PAC file, leading to proxy routing failures. This often resulted in direct connections, bypassing our security policies.

Despite trying various workarounds—adjusting file permissions, testing different share paths, and experimenting with multiple browsers—the local drive approach was unreliable. The lesson was clear: PAC files need a robust hosting solution.

The Turning Point: Hosting the PAC File on a Web Server

After several failed attempts, I moved the PAC file to a web server, hosting it at http://server1.generic.local/proxy.pac using a simple Nginx server. The configuration was minimal:

nginx

server {
    listen 80;
    server_name server1.generic.local;
    root /var/www/html;
    location /proxy.pac {
        try_files /proxy.pac =404;
    }
}

This change was transformative. Here’s why it succeeded:

  1. Reliable Access:
    • Browsers accessed the PAC file via HTTP without the security restrictions of file:// URLs.
    • Remote and VPN users could reach the web server as long as they had network connectivity.
  2. Caching:
    • Browsers cached the PAC file after downloading it, reducing server load. The FindProxyForURL function ran in memory for every request, but the file itself was rarely re-downloaded (typically only at browser startup or network changes).
  3. Ease of Updates:
    • Updating the PAC file was as simple as replacing proxy.pac on the server. Clients adopted changes after their cache expired or on browser restart.
  4. Performance:
    • The web server delivered the PAC file quickly, with lower latency than network shares.
    • However, I noticed some browsing slowness, which I traced to the PAC file’s logic (e.g., dnsResolve latency and slow proxy servers), not the file’s delivery.

Configuring browsers was straightforward:

  • In Chrome/Edge: Settings > System > Proxy Settings > Use PAC file > Enter http://server1.generic.local/proxy.pac.
  • In Firefox: Preferences > Network Settings > Automatic proxy configuration URL > Enter http://server1.generic.local/proxy.pac.

Addressing Performance Issues

While the web server solved accessibility issues, some users reported sluggish browsing. Investigating further, I identified the causes within the FindProxyForURL function:

  • DNS Resolution (dnsResolve): Calling dnsResolve(host) for every request was slow due to our DNS server’s performance.
  • Proxy Chain: The fallback chain (PROXY xproxy…; PROXY yproxy…; DIRECT) caused delays if the primary proxy was unresponsive.
  • Unnecessary Proxying: Traffic to external sites (e.g., google.com) went through the proxy, adding latency.

To optimize, I made these changes:

  • Moved domain checks (e.g., shExpMatch(host, “*.company.local”)) before dnsResolve to skip DNS lookups for bypassed domains.
  • Removed the standby proxy to simplify the fallback chain.
  • Added bypass rules for common external domains (e.g., *.google.com).
  • Switched to a faster DNS server (e.g., 8.8.8.8) to improve dnsResolve performance.

Here’s the optimized PAC file:

javascript

function FindProxyForURL(url, host) {
    var primaryProxy = "PROXY xproxy.company.local:8080";
    var direct = "DIRECT";

    // Bypass domains first to avoid dnsResolve
    if (shExpMatch(host, "*.company.local") || 
        shExpMatch(host, "*.google.com") || 
        shExpMatch(host, "*.facebook.com")) {
        return direct;
    }

    // Resolve IP only if needed
    var resolved_ip = dnsResolve(host);

    // Check IP ranges with null check
    if (resolved_ip && (
        isInNet(resolved_ip, "10.0.0.0", "255.0.0.0") ||
        isInNet(resolved_ip, "172.16.0.0", "255.240.0.0") ||
        isInNet(resolved_ip, "192.168.0.0", "255.255.0.0"))) {
        return direct;
    }

    return primaryProxy + "; " + direct;
}

These changes significantly improved browsing performance, though I’m still monitoring proxy server latency for further optimization.

Ensuring High Availability with F5 LTM

With the PAC file working on a single web server, I recognized that a single point of failure (e.g., server1.generic.local going down) could disrupt proxy routing for new browser sessions or network changes. To ensure high availability, I hosted the PAC file behind an F5 Local Traffic Manager (LTM) load balancer. Here’s how I set it up:

  1. Deployed Multiple Web Servers:
    • Set up two Nginx servers (server1.generic.local and server2.generic.local) hosting proxy.pac at the same path (/proxy.pac).
    • Synced the PAC file across servers to ensure consistency.
  2. Configured F5 LTM:
    • Created a virtual server (pac.generic.local:80) to handle PAC file requests.
    • Set up a pool with both web servers, using a health monitor to check /proxy.pac for a 200 OK response.
    • Used Round Robin load balancing to distribute requests.
    • Configured an HA pair of LTMs for redundancy.
  3. DNS Setup:
    • Created a DNS record for pac.generic.local pointing to the LTM’s virtual IP.
    • Planned a fallback DNS entry (pac-backup.generic.local) for additional redundancy.
  4. Testing:
    • Verified that http://pac.generic.local/proxy.pac was accessible from multiple clients.
    • Simulated server failures (e.g., stopped server1.generic.local) to confirm LTM routed traffic to server2.generic.local.
    • Monitored LTM logs and server access logs to ensure low latency and high availability.
  5. Future Considerations:
    • I’m exploring HTTPS for the PAC file to enhance security (https://pac.generic.local/proxy.pac).
    • For global users, a CDN could cache the PAC file at edge locations, improving performance and reliability.
    • I’m testing browser cache behavior to confirm how often the PAC file is downloaded (typically at startup or network changes).

Lessons Learned

  1. Avoid Local Drives for PAC Files:
    • Local or network share hosting is unreliable due to browser restrictions, latency, and scalability issues. Use a web server for production deployments.
  2. High Availability is Critical:
    • A single web server is a single point of failure. Use a load balancer like F5 LTM with multiple backend servers to ensure the PAC file is always accessible.
  3. Optimize PAC File Logic:
    • Slowness often stems from the FindProxyForURL function. Minimize dnsResolve calls, simplify proxy chains, and bypass unnecessary traffic.
  4. Understand Browser Behavior:
    • Browsers cache the PAC file after downloading it at startup or on network changes. The FindProxyForURL function runs for every request, so optimize its performance.
  5. Test Thoroughly:
    • Test the PAC file with tools like curl (curl –proxy-pac-url http://pac.generic.local/proxy.pac http://example.com) and browser developer tools (chrome://net-internals).
    • Monitor server logs to confirm access patterns and detect issues.

Recommendations for Others

If you’re deploying a PAC file, here’s my advice:

  • Host on a Web Server: Use a reliable web server (e.g., Nginx, Apache) and avoid local drives or network shares.
  • Ensure High Availability: Deploy multiple servers behind a load balancer like F5 LTM. Consider a CDN for global reach.
  • Optimize Performance: Minimize DNS lookups, use fast proxy servers, and bypass common domains in the PAC file.
  • Monitor and Test: Regularly check server logs, test failover scenarios, and profile proxy performance.
  • Secure the PAC File: Use HTTPS and access controls to protect the PAC file from tampering or unauthorized access.

Conclusion

Deploying a PAC file was a journey of trial and error, but moving from a failed local drive approach to a high-availability web server setup was worth the effort. By hosting the PAC file behind F5 LTM and optimizing its logic, we achieved reliable proxy routing with improved performance. I’m still fine-tuning the setup, but these lessons have strengthened our network infrastructure.

Leave a Comment