๐ŸŽ‰ Premium Proxies ยท 3-Day Free TrialClaim Now โ†’
Proxy Basic

What Is a PAC File? Proxy Auto-Config Explained

A PAC file is a JavaScript FindProxyForURL function that tells a browser which proxy to use per URL. Learn PAC syntax, helper functions, WPAD, and security.

S SparkProxy 2 13 min read
Share
What Is a PAC File? Proxy Auto-Config Explained

A PAC file is a small text file holding one JavaScript function, FindProxyForURL(url, host), that a browser or operating system runs for every request to decide which proxy to use. Instead of hardcoding one proxy for all traffic, you can send internal hosts direct, route one domain through proxy A, and everything else through proxy B, all from a single rule set. This guide covers what a PAC file is, the exact function contract, the return values and helper functions, a working proxy.pac example, WPAD auto-discovery, deployment, and the security traps that catch teams every year.

What Is a PAC File?

PAC stands for Proxy Auto-Config. A PAC file is a plain-text JavaScript file, usually named proxy.pac or wpad.dat, that a client evaluates to answer one question per request: "for this URL, do I connect directly or through a proxy, and if so, which one?"

Netscape introduced the format in 1996 with Navigator 2.0. There is no formal RFC for it, so the original Netscape specification remains the de facto standard, and every major browser plus Windows, macOS, and most HTTP libraries still honor it. The file has to define exactly one function with a fixed name and signature. The client calls that function, reads the string it returns, and connects accordingly.

Because the logic is real JavaScript, a PAC file can make decisions that a static proxy setting cannot: match a hostname pattern, check whether a target sits inside a private subnet, fall back to a backup proxy, or route differently by time of day. That flexibility is the whole reason the format outlived its origin.

The FindProxyForURL Function

Every PAC file must define this function, and nothing else is required:

function FindProxyForURL(url, host) {
    return "DIRECT";
}

The client passes two arguments on each request:

  • url is the full URL being requested, for example https://www.sparkproxy.io/docs.
  • host is the hostname pulled out of that URL, for example sparkproxy.io, with no scheme and no port.

host is a convenience so you don't have to parse it out of url yourself. Note one modern behavior that trips people up: for https:// requests, current browsers strip the path and query from url before handing it to your function, so you cannot reliably match on the path of an HTTPS URL. Chrome has done this by default since version 75 (2019) under the PacHttpsUrlStrippingEnabled policy, for privacy reasons covered in the security section below. Match on host for HTTPS, not on the full path.

The function runs top to bottom and returns the first proxy string your logic reaches. Whatever it returns is a directive, not a suggestion.

Free trial

Scraping at scale? Skip the blocks.

Fast, unblockable datacentre proxies with unlimited bandwidth.

PAC Return Values: DIRECT, PROXY, SOCKS

FindProxyForURL returns a string. The string is one or more space-separated directives, and a list is separated by semicolons for fallback.

Return stringWhat the client does
`"DIRECT"`Connect straight to the target, no proxy
`"PROXY host:port"`Use this HTTP proxy
`"HTTPS host:port"`Reach the proxy over TLS (Chrome and modern browsers)
`"SOCKS host:port"`Use a SOCKS4 proxy
`"SOCKS5 host:port"`Use a SOCKS5 proxy (Firefox, Chrome)
`"PROXY a:8080; PROXY b:8080; DIRECT"`Try a, then b, then go direct

The fallback list is the useful part. "PROXY proxy-1.sparkproxy.io:8080; PROXY proxy-2.sparkproxy.io:8080; DIRECT" tells the client to try the primary proxy first, drop to the backup if the primary refuses the connection, and only then connect directly. Order matters, and the client works left to right.

Which keyword you use has to match a protocol the target proxy actually speaks. SOCKS5 behaves very differently from PROXY, and the port conventions differ too. If you are unsure which protocol fits, understanding proxy protocols: HTTP, HTTPS, and SOCKS5 breaks down the tradeoffs, and proxy ports explained covers why 8080 and 1080 show up in these strings.

PAC Helper Functions

The PAC runtime gives you a set of predefined helper functions so you rarely need to write string parsing by hand. These are the ones you will actually use:

FunctionWhat it returnsExample
`isPlainHostName(host)`true when `host` has no dots (a bare intranet name)`isPlainHostName("payroll")`
`dnsDomainIs(host, domain)`true when `host` sits inside `domain``dnsDomainIs(host, ".sparkproxy.io")`
`shExpMatch(str, shexp)`true on a shell-glob wildcard match`shExpMatch(url, "*://*.sparkproxy.io/*")`
`isInNet(host, ip, mask)`true when the host's IP falls in the subnet`isInNet(host, "10.0.0.0", "255.0.0.0")`
`myIpAddress()`the client's own IP as a string`isInNet(myIpAddress(), "192.168.0.0", "255.255.0.0")`
`dnsResolve(host)`the host's IP as a string (runs a DNS lookup)`dnsResolve("proxy.sparkproxy.io")`
`isResolvable(host)`true when DNS resolves the host`isResolvable(host)`
`weekdayRange(d1, d2)`true when today falls in the range`weekdayRange("MON", "FRI")`

One performance warning that most tutorials skip: isInNet with a hostname, dnsResolve, and isResolvable all trigger a synchronous DNS lookup. That function runs for every single request the browser makes, so a PAC that calls dnsResolve on the hot path can add latency to all browsing. Prefer string tests like dnsDomainIs and shExpMatch, and reach for DNS-based helpers only when a hostname test genuinely cannot answer the question.

Windows also ships IPv6-aware variants (isInNetEx, dnsResolveEx, myIpAddressEx), which matter once your network is dual-stack.

A Real proxy.pac Example

Here is a complete, working PAC file that handles the three cases most corporate networks need:

function FindProxyForURL(url, host) {
    // 1. Internal hosts go direct, never through the proxy
    if (isPlainHostName(host) ||
        dnsDomainIs(host, ".internal.sparkproxy.io") ||
        isInNet(host, "10.0.0.0", "255.0.0.0")) {
        return "DIRECT";
    }

    // 2. Send one domain over SOCKS5
    if (shExpMatch(url, "*://*.sparkproxy.io/*")) {
        return "SOCKS5 socks.sparkproxy.io:1080";
    }

    // 3. Everything else: primary proxy, then backup, then direct
    return "PROXY proxy-1.sparkproxy.io:8080; " +
           "PROXY proxy-2.sparkproxy.io:8080; " +
           "DIRECT";
}

Read it top to bottom, which is exactly how the client reads it. Bare hostnames, the internal domain, and the 10.0.0.0/8 range skip the proxy entirely. Anything under sparkproxy.io goes over SOCKS5. Everything else takes the HTTP proxy with a backup and a final direct fallback so a proxy outage does not black out the whole browser.

You can layer in time rules where they help. This snippet sends all traffic direct on weekends:

if (weekdayRange("SAT", "SUN")) {
    return "DIRECT";
}

Keep the whole file short. There is no way to import libraries, and long loops or heavy logic run on every request.

WPAD: Automatic PAC Discovery

Handing a PAC URL to every laptop by hand does not scale. WPAD, the Web Proxy Auto-Discovery Protocol, lets clients find the PAC file on their own. When a browser is set to "automatically detect settings," it looks for the PAC file in two ways:

  1. DHCP. The client asks the DHCP server for option 252, and the server hands back a PAC URL. This method takes priority.
  2. DNS. If DHCP has nothing, the client walks up its own domain trying http://wpad./wpad.dat. A client on east.sparkproxy.io tries wpad.east.sparkproxy.io, then wpad.sparkproxy.io, and so on.

The file WPAD serves is the same FindProxyForURL contract, conventionally named wpad.dat. WPAD itself was only ever an IETF draft (1999) that never became a ratified standard, yet every major browser and Windows implement it. That gap between "widely deployed" and "never standardized" is exactly why its security behavior varies and needs attention.

How to Deploy a PAC File

Getting a PAC file live is three steps.

Host it. Put the .pac file on any web server and serve it with the MIME type application/x-ns-proxy-autoconfig. Serve it over HTTPS from a fixed URL so nobody on the network can swap it in transit.

Point clients at it. For a single machine, paste the URL into the browser or OS field labeled "Use automatic proxy configuration URL" (or "Automatic proxy configuration URL" on the system network settings). For a fleet, push it: Group Policy on Windows, a configuration profile over MDM on macOS, or the equivalent on managed Linux.

Test it. Load a target that should be proxied and one that should go direct, and confirm each takes the path you expect. A PAC evaluates on every request, so a syntax error or a slow dnsResolve call shows up as sluggish or broken browsing across the board, not as one clean error.

Since a PAC only selects the proxy host and port, it cannot supply a username or password. Proxy credentials are negotiated separately after the client connects, which is worth understanding before you deploy; see how proxy authentication works.

PAC File Security Caveats

A PAC file decides where every byte of a user's web traffic goes, so it is a high-value target. Three risks deserve real attention.

Rogue WPAD. Because DNS-based WPAD trusts whatever answers the wpad lookup, anyone who can respond first can serve a malicious PAC that routes all traffic through their proxy, which is a clean man-in-the-middle. US-CERT documented this in alert TA16-144A (May 2016), covering the "WPAD name collision" problem where internal wpad queries leak to public DNS and get answered by a stranger. If you don't use WPAD, disable "automatically detect settings" on your clients. If you do, register the internal wpad host explicitly and lock it down.

URL leakage. FindProxyForURL receives the request url. Older engines passed the full HTTPS path and query into the PAC, and a hostile PAC could exfiltrate that through dnsResolve. This is why modern browsers strip the path and query from https:// URLs before calling the PAC. Chrome does it by default since version 75. The takeaway for authors: never rely on matching sensitive HTTPS paths in a PAC, because the browser may not give you the path at all.

Untrusted source. The PAC is JavaScript that runs inside the client's proxy engine. On Windows that engine has historically been an exploit target, so treat the PAC URL and its contents as security-critical: pin the URL, serve over HTTPS, and control who can edit the file. A PAC that decides your routing is functionally part of your attack surface, which is a useful frame borrowed from how a forward proxy concentrates control at one point.

PAC Files and Web Scraping

Here is the honest boundary most articles skip: a PAC file is a browser and operating-system routing tool, not a scraping tool. Automation code does not read PAC files by default, and you gain nothing by wrapping proxy logic in JavaScript that your script has to run per request. In a scraper you set the proxy directly.

import requests

proxies = {
    "http":  "http://USER:PASS@proxy.sparkproxy.io:8080",
    "https": "http://USER:PASS@proxy.sparkproxy.io:8080",
}
r = requests.get("https://www.sparkproxy.io", proxies=proxies, timeout=30)
print(r.status_code)

When the target runs serious bot detection and you would rather not manage rotation, rendering, and retries yourself, the SparkProxy Scraping API wraps the proxy pool behind one endpoint. Authenticate with the X-API-Key header and pass the target as the url parameter:

import requests

resp = requests.get(
    "https://scrape.sparkproxy.io/api/v1",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={
        "url": "https://www.sparkproxy.io",
        "render_js": "true",
        "country_code": "US",
    },
)
print(resp.status_code)
print(resp.text[:500])

render_js defaults to true and format defaults to html; the full parameter list is in the SparkProxy Scraping API docs. Use a PAC file to route the browsers your team actually sits in front of. Use direct proxy settings or the API for everything you automate.

Frequently asked questions

FAQ

A PAC file is used to tell a browser or operating system which proxy to use for each request, based on rules you write in JavaScript. It lets you send internal traffic direct, route specific domains through specific proxies, add backup proxies, and change routing by time or subnet, all from one file instead of a single fixed proxy setting.

FindProxyForURL(url, host) is the one function every PAC file must define. The client calls it on each request, passing the full URL and the hostname, and reads the string it returns. That string tells the client whether to connect directly (DIRECT) or through a named proxy (PROXY host:port, SOCKS5 host:port, and so on).

A PAC file is the JavaScript file that holds the routing logic. WPAD (Web Proxy Auto-Discovery) is the protocol clients use to find that file automatically, via DHCP option 252 or a DNS lookup for wpad./wpad.dat. In short, WPAD is how the client discovers the PAC file, and the PAC file is what actually makes the proxy decision. A WPAD PAC file is just a PAC served at the conventional wpad.dat location.

proxy.pac is the conventional filename for a manually configured PAC file, and wpad.dat is the name used for WPAD auto-discovery. Both are hosted on a web server and served with the MIME type application/x-ns-proxy-autoconfig. Clients fetch the file from that URL rather than storing routing logic locally.

They can be. The main risks are a rogue WPAD server serving a malicious PAC that routes traffic through an attacker (documented in US-CERT alert TA16-144A), and URL leakage where the full request URL is exposed to the PAC logic. Serve PAC files over HTTPS from a pinned URL, disable WPAD if you do not use it, and control who can edit the file.

Not usefully. PAC files are a browser and operating-system feature, and scraping scripts do not read them by default. For automation you set the proxy directly per request, or use a service like the SparkProxy Scraping API that handles proxy selection, rotation, and rendering behind a single endpoint.

Limited-time ยท 50% off

Get 50% off your first purchase

Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.

Offer ends soon โ€” claim it before it's gone

Claim Discount

About the Author

This guide was written by the SparkProxy Technical Team. SparkProxy operates residential, datacenter, and mobile proxy networks and a Scraping API for large-scale web data collection. We publish practical, engineering-focused explainers on proxy configuration, IP reputation, and reliable data collection, grounded in how these systems behave in production rather than in marketing claims.

Keep reading

Related articles

What Is a Rotating Proxy API and How It Works

What Is a Rotating Proxy API and How It Works

A rotating proxy API gives you one endpoint that serves a fresh IP per request or sticky sessions, so you never manage a proxy list. Here is how it works.

SparkProxyยทProxy Basic