Block 16,024+ AI-tool domains at the browser level with zero client software. Deploy via WPAD, GPO, or MDM using our daily-updated domain feed.
Many organizations need to block AI tools but lack a forward proxy, SWG, or NGFW with URL filtering. PAC files fill that gap with zero infrastructure.
Every PAC file contains a single JavaScript function: FindProxyForURL(url, host). The browser calls it for every request and routes traffic based on the return value.
| Return String | Behavior |
|---|---|
"DIRECT" | Connect without a proxy |
"PROXY host:port" | Route through an HTTP proxy |
"SOCKS host:port" | Route through a SOCKS proxy |
Returning a proxy address that doesn't exist or serves a block response effectively blocks the request.
dnsDomainIs(host, domain)
Returns true if the hostname ends with the specified domain suffix. Best for AI-tool blocking.
shExpMatch(host, pattern)
Shell-expression matching with wildcard support for flexible pattern matching.
localHostOrDomainIs(host, hostdom)
Matches exact hostnames or subdomains without substring false positives.
PROXY 127.0.0.1:9
Routes to discard protocol. Functional but shows a confusing connection-refused error to users.
PROXY block.internal:8080
Lightweight HTTP server returning a branded block page with policy explanation and exception request link.
// PAC file: Block AI tools, allow everything else // Save as proxy.pac or wpad.dat and host on an internal web server function FindProxyForURL(url, host) { // Normalize hostname to lowercase host = host.toLowerCase(); // --- AI Tool Domains to Block --- // Text & Language AI if (dnsDomainIs(host, "openai.com") || dnsDomainIs(host, "chat.openai.com") || dnsDomainIs(host, "chatgpt.com") || dnsDomainIs(host, "anthropic.com") || dnsDomainIs(host, "claude.ai") || dnsDomainIs(host, "gemini.google.com") || dnsDomainIs(host, "bard.google.com") || dnsDomainIs(host, "perplexity.ai") || dnsDomainIs(host, "you.com") || dnsDomainIs(host, "poe.com") || dnsDomainIs(host, "writesonic.com") || dnsDomainIs(host, "jasper.ai") || dnsDomainIs(host, "copy.ai")) { return "PROXY block.internal.corp:8080"; } // Code & Development AI if (dnsDomainIs(host, "github.com") && shExpMatch(url, "*copilot*")) { return "PROXY block.internal.corp:8080"; } if (dnsDomainIs(host, "copilot.github.com") || dnsDomainIs(host, "codeium.com") || dnsDomainIs(host, "cursor.sh") || dnsDomainIs(host, "tabnine.com") || dnsDomainIs(host, "replit.com")) { return "PROXY block.internal.corp:8080"; } // Image & Visual AI if (dnsDomainIs(host, "midjourney.com") || dnsDomainIs(host, "stability.ai") || dnsDomainIs(host, "leonardo.ai") || dnsDomainIs(host, "dall-e.com") || dnsDomainIs(host, "deepai.org")) { return "PROXY block.internal.corp:8080"; } // All other traffic: connect directly return "DIRECT"; }
The example above is a simplified static PAC file. In production, generate the PAC file dynamically from our API or use a hash-based lookup to match 16,024+ domains efficiently.
PAC files execute in a sandboxed JavaScript environment inside the browser. The function is called for every URL request, including embedded resources. For large domain lists, use a sorted array with binary search or a hash-set lookup to keep resolution time O(log n) or O(1) instead of O(n).
PROXY 127.0.0.1:9 — functional but shows a confusing connection-refused error.Static PAC files with hardcoded domains become stale as new AI tools launch daily. Dynamic generation solves this with a server-side script that fetches current domains from our API.
The browser fetches the PAC file on startup and at configurable intervals (typically every few hours), ensuring a current blocklist without manual intervention.
The PHP script below acts as a dynamic PAC file generator. Host it on an internal web server and point your WPAD or GPO configuration to its URL. It caches the API response for one hour to avoid rate limits.
# Dynamic PAC file generator (PHP) # Host at: http://wpad.corp.example.com/wpad.dat # Fetches AI domains from the blocklist API, caches for 1 hour <?php header('Content-Type: application/x-ns-proxy-autoconfig'); header('Cache-Control: max-age=3600'); $cacheFile = '/tmp/ai-blocklist-cache.json'; $cacheTTL = 3600; if (!file_exists($cacheFile) || time() - filemtime($cacheFile) > $cacheTTL) { $response = file_get_contents( 'https://api.aitoolsblocklist.com/v1/domains?format=list&key=YOUR_KEY' ); file_put_contents($cacheFile, $response); } else { $response = file_get_contents($cacheFile); } $domains = array_filter(explode("\n", trim($response))); // Build the dnsDomainIs() conditions $conditions = []; foreach ($domains as $d) { $d = trim(strtolower($d)); if ($d) $conditions[] = 'dnsDomainIs(h,"'.$d.'")'; } echo "function FindProxyForURL(url, host) {\n"; echo " var h = host.toLowerCase();\n"; echo " if (" . implode(" ||\n ", $conditions) . ") {\n"; echo " return \"PROXY block.internal.corp:8080\";\n"; echo " }\n"; echo " return \"DIRECT\";\n"; echo "}\n"; ?>
This approach scales to tens of thousands of domains. For even better performance with very large lists, use a hash-set approach where domains are checked with the in operator for O(1) lookups.
application/x-ns-proxy-autoconfig. IE and legacy Edge silently ignore files served with incorrect types.
AddType application/x-ns-proxy-autoconfig .pac .dat to your configuration.
mime.types file or use default_type in the location block.
.pac and .dat extensions.
WPAD lets browsers discover and download PAC files automatically with zero client-side configuration. It is enabled by default on Windows.
wpad.<domain> and downloads /wpad.dat from that host.
Every Windows machine with default settings, every macOS machine with "Auto Proxy Discovery" enabled, and every managed Chromebook will automatically discover your AI-blocking PAC file.
http://wpad.corp.example.com/wpad.dat.
option wpad-url code 252 = text; then set option wpad-url "http://wpad.corp.example.com/wpad.dat"; in the subnet declaration.
Create an A record for wpad in your internal DNS zone pointing to the web server hosting your PAC file. The browser constructs http://wpad.<domain>/wpad.dat automatically.
For example, if the client's DNS suffix is corp.example.com, it tries http://wpad.corp.example.com/wpad.dat. Ensure the server responds with the correct MIME type.
# DNS configuration for WPAD (BIND zone file) # Add this A record to your internal DNS zone wpad IN A 10.0.1.50 # If using CNAME (less common but works in some environments): wpad IN CNAME internal-webserver.corp.example.com. # --- Web server configuration --- # Apache: Add MIME type for PAC files # /etc/apache2/sites-enabled/wpad.conf <VirtualHost *:80> ServerName wpad.corp.example.com DocumentRoot /var/www/wpad AddType application/x-ns-proxy-autoconfig .dat .pac <Directory /var/www/wpad> Options -Indexes AllowOverride None Require all granted </Directory> </VirtualHost> # Nginx: Add MIME type and serve wpad.dat # /etc/nginx/conf.d/wpad.conf server { listen 80; server_name wpad.corp.example.com; root /var/www/wpad; location = /wpad.dat { types { } default_type application/x-ns-proxy-autoconfig; } } # IIS: Add MIME mapping via PowerShell Add-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' ` -Filter "system.webServer/staticContent" ` -Name "." ` -Value @{ fileExtension = '.dat' mimeType = 'application/x-ns-proxy-autoconfig' }
The WPAD server is a critical dependency. Chrome and Edge cache the last-known PAC file and fall back to DIRECT if it's unavailable. Firefox retries periodically. Deploy behind a load balancer or use DNS round-robin for redundancy.
If an attacker registers the wpad DNS name or responds to DHCP option 252, they can serve a malicious PAC file. Mitigate by explicitly registering the hostname in DNS, blocking external WPAD queries at the firewall, and using DHCP snooping.
Chrome re-fetches the PAC file on network changes and periodically. Firefox respects the HTTP Cache-Control header. Set max-age=3600 on PAC file responses to ensure hourly updates without excessive polling.
Explicit PAC file assignment via GPO or MDM is more deterministic than WPAD. It guarantees application regardless of DHCP/DNS configuration and is easier to audit.
GPO sets the proxy auto-configuration URL in Windows Internet Settings. This applies to IE, legacy Edge, Chromium Edge, and Chrome (which reads system proxy settings by default).
# GPO Registry Settings for PAC File # Path: User Configuration > Preferences > Windows Settings > Registry # Enable automatic proxy configuration Key: HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings Value: AutoConfigURL Type: REG_SZ Data: http://wpad.corp.example.com/wpad.dat # Disable "Automatically detect settings" to prevent WPAD override Key: HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections Value: DefaultConnectionSettings # Byte 8 bit 4: 0 = disable auto-detect, 1 = enable # --- Chrome-specific policy (if not using system settings) --- # ADMX: Google Chrome > Proxy Settings Key: HKLM\Software\Policies\Google\Chrome Value: ProxyPacUrl Type: REG_SZ Data: http://wpad.corp.example.com/wpad.dat Key: HKLM\Software\Policies\Google\Chrome Value: ProxyMode Type: REG_SZ Data: pac_script # --- Edge Chromium-specific policy --- Key: HKLM\Software\Policies\Microsoft\Edge Value: ProxyPacUrl Type: REG_SZ Data: http://wpad.corp.example.com/wpad.dat Key: HKLM\Software\Policies\Microsoft\Edge Value: ProxyMode Type: REG_SZ Data: pac_script # --- PowerShell: Apply PAC URL to all users immediately --- $pac = "http://wpad.corp.example.com/wpad.dat" Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" ` -Name "AutoConfigURL" -Value $pac # Verify the setting was applied Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" ` -Name "AutoConfigURL"
ProxyAutoConfigURLString in the com.apple.SystemConfiguration domain. Alternatively, use a Jamf-managed network profile per Wi-Fi SSID.
policies.json file to /Applications/Firefox.app/Contents/Resources/distribution/ or configure via autoconfig.js.
GPO is the most robust method for domain-joined machines. Set PAC URL via registry preferences (not deprecated IE Maintenance). For non-domain machines, use Intune or a PowerShell login script. Chrome and Edge both respect system proxy settings unless browser-specific policies override.
MDM configuration profiles enforce PAC URLs at the system level. Safari and Chrome respect the system setting. Firefox uses its own proxy stack and requires a separate policy — deploy a policies.json file to /Applications/Firefox.app/Contents/Resources/distribution/ with the WebProxy > AutoConfigURL key pointing to your PAC file.
Browsers handle PAC files differently in caching, failover, DNS resolution, and error handling. Understanding these differences is essential for reliable cross-browser deployment.
dnsResolve() may differ from other browsers.--proxy-pac-url flag or ProxyPacUrl enterprise policy.HTTPS proxy types in PAC return values.about:preferences > Network Settings or autoconfig.js.dnsResolve() to prevent timeouts.const, let, arrow functions, or template literals.var declarations and traditional function syntax.ProxyMode and ProxyPacUrl Edge enterprise policies.var instead of let/const, traditional function declarations, no arrow functions or template literals
dnsResolve() — introduces DNS latency on every request and behaves differently across browsers
dnsDomainIs() and shExpMatch() — string comparison only, no DNS overhead
Array.includes() or Array.indexOf(); use a for-loop instead
FindProxyForURL returns undefined, Chrome falls back to DIRECT but older browsers may block all traffic
Chaining thousands of dnsDomainIs() calls becomes unwieldy and slow. The PAC sandbox lacks Set and Map, but plain JavaScript objects enable efficient lookups.
The best approach is a hash-set using a JavaScript object. Domains become keys, and each request checks hostname existence via the [] operator for O(1) average-case lookup time.
// High-performance PAC file with hash-set domain lookup // Handles 40,000+ domains with O(1) lookup time var BLOCKED = { "openai.com": 1, "chatgpt.com": 1, "anthropic.com": 1, "claude.ai": 1, "gemini.google.com": 1, "midjourney.com": 1, "stability.ai": 1, "perplexity.ai": 1, "cursor.sh": 1, "copilot.github.com": 1, "codeium.com": 1, "writesonic.com": 1, "jasper.ai": 1, "copy.ai": 1, "replit.com": 1, "huggingface.co": 1, "leonardo.ai": 1, "poe.com": 1 // ... 40,000+ entries generated by API }; function FindProxyForURL(url, host) { var h = host.toLowerCase(); // Direct lookup if (BLOCKED[h]) { return "PROXY block.internal.corp:8080"; } // Walk up domain hierarchy for subdomain matching // e.g., "api.openai.com" matches "openai.com" var parts = h.split("."); for (var i = 1; i < parts.length - 1; i++) { var parent = parts.slice(i).join("."); if (BLOCKED[parent]) { return "PROXY block.internal.corp:8080"; } } return "DIRECT"; }
This pattern handles subdomain matching automatically. When a user navigates to api.openai.com, the function walks up the hierarchy and matches openai.com. The loop stops before checking the TLD to avoid false positives.
1 values to enable per-category policies. Our API outputs domains with category tags, and the dynamic PAC generator can produce files that check category membership via bitmask or integer code.
PAC file enforcement happens in the browser, not on a network device. There is no centralized log of blocked requests by default — your monitoring strategy depends on your block response approach.
| Approach | Visibility | User Experience | Cost |
|---|---|---|---|
| Block-page proxy | Full audit trail — timestamp, client IP, hostname | Branded block page with policy explanation | Single nginx instance |
Loopback (127.0.0.1:9) |
No logging — no server to record the request | Confusing connection-refused error | Zero |
chrome://net-internals/#proxy — shows PAC URL, download status, and FindProxyForURL() results.about:networking#dns and browser console show proxy resolution results.text/html instead of application/x-ns-proxy-autoconfig — browser silently ignores the file.pacparser (open-source PAC evaluator) before deploying.ProxySettingsPerUser to 0 — requires admin rights to change.For comprehensive monitoring, pair PAC file blocking with DNS-level monitoring using our DNS blocklist. DNS queries catch non-browser applications that bypass PAC enforcement entirely.
PAC files are a pragmatic, low-cost solution for AI-tool blocking, but they have inherent limitations. Understand these before relying on PAC files as your sole enforcement mechanism.
curl, AI-tool CLIs, and scripts bypass PAC file enforcement
For content-level enforcement and DLP, you need SSL inspection via a forward proxy or SWG.
Download the sample domain list to test your PAC file setup, or tell us about your environment and we will generate a ready-to-deploy PAC file customized for your infrastructure.
Tell us your proxy infrastructure, browser mix, and deployment method. We will generate a ready-to-deploy PAC file within 24 hours.