# gfw-pac: a PAC file that sends domestic traffic direct and only proxies the rest

> gfw-pac is a proxy auto-config file and its generator, built on public GeoIP data, where domain matching comes first, IP range matching is a radix tree fallback, and a small set of domestic IPv4 and IPv6 ranges is enough to keep most local traffic off the proxy entirely.

**zhiyi7/gfw-pac** — 科学上网 PAC 成品文件以及生成器。可自定义代理域名和直连域名。不在自定义名单里的域名再按IP匹配，CNIP走直连，其他走代理，支持IPv6

- Repository: https://github.com/zhiyi7/gfw-pac
- Stars: 1,269 · Forks: 331
- Language: JavaScript
- License: NOASSERTION
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/zhiyi7-gfw-pac

## What the PAC file decides, and in what order

gfw-pac is a set of PAC, proxy auto-config, files plus a generator for them. The generated file routes traffic by custom domain lists, and for anything not in those lists it falls back to matching on the resolved IP address, where a domestic address goes direct and everything else goes through the proxy. IPv6 is supported.

The ordering is the design. The project lists the features as IP rules first: if the IP a domain resolves to is domestic, the result is direct and the traffic never goes through the proxy program. Then speed: domain matching is preferred, because the high frequency domains save resolution time, and the IP range matching uses a radix tree with a time complexity of only O(m), where m is at most 32 for IPv4 or at most 128 for IPv6.

That radix tree bound is the whole point of the IP fallback. A linear scan over a domestic IP list would be thousands of comparisons per request, so the fallback is only viable because the structure makes it a bounded walk rather than a sweep, and because the fallback is rarely reached at all for the common case of a known domain.

The third feature is restraint. Only a very small number of high frequency domains are added, to reduce the chance of traffic going the wrong way. A PAC file that listed every domestic domain would be more accurate in principle and unmaintainable in practice, so the list is deliberately short and the IP list does the heavy lifting.

IPv6 is supported too, which the documentation describes as no longer requiring you to turn off IPv6 or AAAA resolution for the IP rules to work, so you can leave IPv6 on and benefit from it. Pure IP addresses are handled correctly as well, so an app using DNS over HTTPS works fully.

## Why a PAC file at all, when the client has routing rules

The documentation asks a fair question and answers it. Modern systems, macOS, iOS and Windows, have a network framework that executes PAC policy automatically, so most applications' HTTP requests can use the proxy and not just the browser.

If you rely on the proxy tool's routing rules instead, all traffic enters the proxy program, and even traffic that matches a direct rule still has to be forwarded by the proxy program, which affects performance. If the network framework decides through a PAC file first whether to proxy or go direct, then direct traffic no longer passes through the proxy program at all, and performance is better.

So the argument is not that PAC is a better rule language. It is that the decision happens one layer earlier, in the operating system, before the traffic is handed to the proxy client at all.

The project also notes that all current popular proxy front ends have PAC support built in, and that when you select the PAC mode in such a front end, it sets the system proxy script to a PAC file it generates itself. In other words, the PAC approach is not a workaround for clients that lack it, it is a mode they ship.

And the second section of the documentation makes the differentiation argument. Maintaining a PAC file is not a priority for proxy tools, so the ones they ship tend to accumulate ad filtering and privacy protection and grow enormous domain lists, and over the years they decay into missing or misrouting things. The other extreme is a tool with very few rules, where most traffic still goes through the client, and IPv6 is not supported at all. This file adds only the most frequently visited domains, and for domains not in the list it confirms with an IP range match, which the documentation says keeps more than 99 percent of domestic traffic out of the proxy program.

## The generator, and the four files that configure it

The generator is a Python script with a small option set, and the usage line gives the whole interface.

```
usage: gfw-pac.py -f 输出的PAC文件名 -p 代理服务器 [-h]
                  [--proxy-domains 自定义使用代理域名的文件]
                  [--direct-domains 自定义直连域名域名的文件]
                  [--localtld-domains 本地TLD文件]
                  [--ip-file 从 Loyalsoldier/geoip/blob/release 中下载的 text/cn.txt 文件]
```

Both -f and -p are required. -f is the output PAC file name and -p is the proxy server, given in PAC syntax, for example PROXY 192.168.1.1:3128.

The three optional domain files are one domain per line. proxy-domains is the list of domains that should go through the proxy, direct-domains is the list that should go direct, and localtld-domains is the list of direct top level domains, where each line must carry a leading dot, for example .test. The leading dot is required because a TLD rule and a hostname rule are different things, and a bare test would also match a domain called test in a way you did not intend.

The fourth option, --ip-file, takes the text/cn.txt file downloaded from the Loyalsoldier/geoip release, which is the source of the domestic IP ranges.

The example invocation shows all four together:

```
./gfw-pac.py -f gfw.pac \
               -p "PROXY 192.168.1.200:3128; DIRECT" \
               --proxy-domains=proxy-domains.txt \
               --direct-domains=direct-domains.txt \
               --localtld-domains=local-tlds.txt \
               --ip-file=cidrs-cn.txt
```

Note the proxy value in the example carries a DIRECT fallback after the semicolon, which is the PAC way of saying what to do if the proxy is unreachable.

The repository holds the data files alongside the script, with proxy-domains.txt, direct-domains.txt, local-tlds.txt and cidrs-cn.txt, plus the pac-template that the generator fills in, the generated gfw.pac, and test_pac_execution.js, which is a test that the generated file actually executes correctly.

## The weekly sync, and the limits the documentation admits

The repository is updated automatically. Every Thursday a GitHub Action syncs the data from the Loyalsoldier/geoip repository and regenerates the gfw.pac file, and the release list shows the pattern, with tags like v20261015.1, v20261008.1 and v20261001.1, one per week.

That weekly cadence is a design constraint on the user. The data is a snapshot, and between snapshots it ages, and the documentation gives a tip that is really a warning: come back often to download the PAC file containing the latest data.

The second tip is the one that decides whether the tool works at all in a given environment. Solve DNS pollution yourself. The IP fallback depends on the name resolving to a real address, and if the resolution is polluted the fallback decides on the wrong address, and a polluted domestic name pointing overseas would be proxied instead of going direct. The tool does not attempt to fix that, it assumes you have.

The third tip is that the proxy tool should also configure GEOIP and GEOSITE style routing rules, on the condition that the data is kept up to date. So the PAC file is a complement to the client's own rules rather than a replacement for them.

There is also an honesty constraint in the usage section that is worth reading. The recommended path is to download gfw.pac and edit its first line to point at your own proxy server. The alternative is to run the generator by hand, which allows custom domain flow and is more flexible, but the documentation notes that most clients can now define domain matching rules themselves, so special domains are better handled by configuring the client.

The distribution story is a set of compiled artifacts rather than a library. The project is not a package you import, it is a file you point your system at, and the description covers a range of clients, with the topic tags listing clash, clash-meta, shadowrocket, surge, trojan, v2ray, vless, xray, hysteria and geoip alongside the PAC and proxy ones. The documentation also says the generated file is small, that the script is entirely ES5 so most modern systems can execute it, and that it works on iOS, macOS, Windows and Android across Chrome, Edge and Firefox.

## Conclusion

gfw-pac fits a client on a system whose network framework supports PAC, where you want domestic traffic to bypass the proxy process rather than being forwarded through it. It is a poor fit if DNS resolution happens somewhere you cannot control, because the IP fallback depends on resolving the name and the documentation is explicit that you have to solve DNS pollution yourself. Before using it, replace the proxy line in the first line of gfw.pac with your own server, and check whether the domain lists shipped in the repository are current for the sites you actually use.

## FAQ

### What is the gfw-pac project?

It is a set of PAC proxy auto-config files plus a generator, gfw-pac.py, that produces them. The generated file matches on your custom domain lists first, and for anything not listed it falls back to matching the resolved IP address, where a domestic address goes direct and everything else goes through the proxy, with IPv6 supported.

### How do I generate my own PAC file with gfw-pac?

Run the generator with -f for the output file name and -p for the proxy server, both required, and optionally --proxy-domains, --direct-domains, --localtld-domains and --ip-file, each taking a file with one entry per line. The local TLD file entries must carry a leading dot, for example .test, and the IP file is the text/cn.txt file from the Loyalsoldier/geoip release.

### Why use a PAC file when my proxy client already has routing rules?

Because the operating system network framework on macOS, iOS and Windows executes PAC policy automatically, so direct traffic never enters the proxy program at all. With a client's own routing rules, all traffic goes to the proxy program and even direct-matched traffic has to be forwarded by it, which costs performance.

### How often is the gfw-pac data updated?

The repository is synced automatically every Thursday by a GitHub Action that pulls the data from Loyalsoldier/geoip and regenerates gfw.pac, which is why the release tags are dated weekly. The documentation advises downloading the file again often to get the latest data, and it notes that you have to solve DNS pollution yourself, because the IP fallback depends on a correct resolution.

## Sources

- [Issues](https://github.com/zhiyi7/gfw-pac/issues)
- [README](https://github.com/zhiyi7/gfw-pac/blob/master/README.md)
- [Releases](https://github.com/zhiyi7/gfw-pac/releases)
- [zhiyi7/gfw-pac on GitHub](https://github.com/zhiyi7/gfw-pac)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/zhiyi7-gfw-pac
