作为一个在基础设施圈子里摸爬滚打了十来年的人,我始终觉得 squid 是个被严重低估的组件。它没有花哨的管理界面,没有热门的指标大盘,但靠着精简的配置和极高的吞吐能力,至今还在大量企业的网络出口、Web 入口和缓存卸载场景里扛着最核心的流量。今天想把这套东西掰开揉碎聊一聊:squid 的几种典型工作模式分别适合什么场景,配置上要注意哪些坑,以及上线后你会遇到哪些稀奇古怪的问题。
squid 本质上是一台内容缓存与转发服务,处在一端用户与另一端资源服务器之间。它能做缓存、能限定访问范围、能改写请求头、能对后端做负载分配。很多朋友一提到它就条件反射地想到正向出口缓存,但实际上它还能承担入口侧的流量接入与缓存卸载,甚至可以在用户完全无感知的情况下,在网络层把流量“接”进来再转发出去。这篇文章我会把三种主流模式从头到尾讲透:正向模式、反向接入模式、透明接管模式。适合刚接触网络中间层的运维新手,也适合想优化内网带宽、给 Web 集群减负的工程师参考。
1. 三种主流工作模式:先看清适用边界
1.1 正向模式:帮内网用户省流量
这是最传统的用法。当你把 squid 放在企业内网出口,客户端(浏览器、扫码设备、办公软件)把请求发给 squid,由 squid 代替客户端去访问目标站点,拿到资源后一方面返回给客户端,另一方面在本地磁盘或内存中留一份副本。下次有别的客户端请求相同资源,squid 直接从缓存返回,不再绕道外网。
我见过最典型的案例是某制造企业的办公网,几百号人每天都要重复访问同一批系统更新包、软件仓库、培训视频链接。原先这些流量全部穿透出口带宽,一条 500M 的链路被吃满,员工还抱怨网页卡顿。上了 squid 正向模式之后,重复资源的命中率能做到 40% 以上,出口链路压力立刻降了一个量级。这模式的核心价值就是“帮内网用户在重复请求中节省流量”,同时还能做访问名单控制,比如只允许特定网段访问特定域名,其他的一律拒绝。
1.2 反向接入模式:帮 Web 站点挡压力
反向接入模式的意思是 squid 站在 Web 服务器前面,对外接收用户请求,转发到后端的真实源站,并把源站返回的内容缓存下来。这里的用户一般来自公网,也可能来自内网。最常用的场景是Web集群:入口只有一个地址,后面挂多台应用服务器,squid 根据规则把不同请求分发到不同节点,同时对静态资源做缓存,减轻应用层压力。
反向接入和正向模式最大的区别在于服务对象:正向模式服务的是“发起请求的客户端”,反向接入服务的是“背后的源站”。我习惯用“前台接待”来理解——squid 是公司前台,访客只需要知道前台的位置,前台再引导你去找具体部门。后端服务器躲在接待台后面,不直接暴露给外部流量,这样既安全又方便做横向扩容。
1.3 透明接管模式:让客户端无感接入
透明接管模式就是把 squid 架设在网关路径上,通过网络层的引流规则(最常用的是 iptables 或路由器策略)把去往特定端口(比如 80)的流量悄无声息地交给 squid。客户端的浏览器不需要配置任何东西,用户完全感受不到中间多了一道关卡。
透明接管经常用在校园网、园区网这种无法逐台配置客户端的场景。它最大的优势就是“零客户端改动”,但对 squid 的配置要求更精细,因为请求的原始信息(比如源 IP、原始目标地址)需要在透明模式下被正确保留和还原,否则后端服务器看到的访问来源全变成 squid 自己的地址,日志和业务都会一团糟。后面我会详细演示这部分的配置要点。
我把三种模式的关键差异整理成了下表,方便你选型时对照。
| 模式 | 服务对象 | 客户端是否需要配置 | 典型位置 | 主要收益 |
|---|---|---|---|---|
| 正向模式 | 内网用户 | 需要 | 内网出口 | 节省带宽、访问控制 |
| 反向接入模式 | 公网/内网访客 | 不需要 | Web 入口 | 隐藏源站、缓存加速 |
| 透明接管模式 | 任意客户端 | 无需感知 | 网关路径 | 免配置引流、集中管控 |
2. 配置文件与核心指令:先打好地基
2.1 主配置文件结构
squid 的所有行为都通过一个文本文件控制,通常位于 /etc/squid/squid.conf。这个文件看起来指令很多,但真正高频使用的不过二三十条。配置的核心哲学是“先定义资源,再定义权限,最后定义动作”:
- 用
http_port定义监听端口和模式 - 用
cache_dir定义缓存存放位置和大小 - 用
acl定义访问名单 - 用
http_access定义允许或拒绝的规则
http_access的匹配是自上而下的,匹配到第一条就停止,所以一定要把严格的拒绝规则放在宽松规则前面。由于这个顺序问题,我见过多次误操作导致整网断流的案例,后面会专门讲排查方法。
下面是一段最基础、能在大多数环境跑起来的配置骨架:
# 定义工作模式与监听端口 http_port 3128 # 缓存目录:分配 10GB 空间,一级目录16个,二级目录256个 cache_dir ufs /var/spool/squid 10240 16 256 # 定义内部网段 acl localnet src 192.168.10.0/24 acl localnet src 10.10.0.0/16 # 定义允许访问的域名 acl allowed_domains dstdomain .example.com acl allowed_domains dstdomain .software-update.example.net # 只要来自内网,就允许访问 http_access allow localnet # 其他情况一律拒绝 http_access deny all2.2 三个容易忽略的细节
第一,cache_dir中的 16 和 256 分别代表第一级和第二级子目录数量。这两个值直接影响磁盘上大量小文件时的查找效率,不建议随意调大。如果缓存文件数量非常大,可以适当增加一级目录数量,但不要超过 32,否则目录遍历反而变慢。
第二,visible_hostname建议显式配置。很多发行版把 squid 编译时的默认主机名写进配置,但如果你改了系统主机名,squid 启动时可能会因为无法解析主机名而报错。我习惯直接写一行:
visible_hostname squid-node-01第三,缓存目录必须在启动前手工初始化。squid 在运行过程中会动态创建子目录,但首次建立顶层缓存目录结构通常需要执行squid -z。否则首次启动可能会报“Failed to verify one of the cache_dirs”之类的错误。
2.3 日志与统计
squid 的日志格式非常稳定,默认访问日志在 /var/log/squid/access.log。每一行都记录着时间戳、客户端 IP、请求方式、请求 URL、响应码、命中状态等关键信息。排查问题时一定要看日志,而不是瞎猜。我常用的三条命令:
# 查看实时访问情况 tail -f /var/log/squid/access.log # 统计最热门的请求目标 awk '{print $7}' /var/log/squid/access.log | sort | uniq -c | sort -rn | head -20 # 统计命中率 grep -c "TCP_HIT" /var/log/squid/access.log日志里的TCP_HIT、TCP_MISS、TCP_MEM_HIT这些状态码能直接告诉你缓存是否生效,绝大部分命中率问题都能从这些字段里找出答案。
3. 正向模式配置实操:从零跑通出口缓存
3.1 规划与准备
在动手之前,先想清楚三件事:监听端口、缓存空间、允许名单。监听端口建议用 3128,这是 squid 的传统端口,不会与 Web 服务冲突。缓存空间根据出口流量来定,我一般建议至少给到磁盘剩余空间的 20%。允许名单则要梳理出内网哪些网段允许使用,以及是否有必须放行的域名。
准备好一台双网卡服务器:一块网卡接内网交换机,另一块接出口网关。squid 本身不需要开启路由转发,因为它只是接收内网客户端发来的请求,再作为客户端去访问外网资源。
3.2 正向模式核心配置
下面是经过我反复验证的正向模式配置,直接拿去改改就能用:
# 监听内网网卡地址,仅允许内网访问 http_port 192.168.10.10:3128 # 缓存目录 20GB cache_dir ufs /data/squid/cache 20480 16 256 # 内存缓存上限,别设太大,避免占用过多业务内存 cache_mem 512 MB # 日志 access_log /var/log/squid/access.log squid cache_log /var/log/squid/cache.log # 定义内网网段 acl boss_net src 192.168.10.0/24 acl office_net src 192.168.20.0/24 # 禁止访问的目标 acl blocked_sites dstdomain .p2p.example.net acl blocked_sites dstdomain .ads.example.com # 规则顺序:先允许内网,再拒绝特殊域名 http_access allow boss_net http_access allow office_net http_access deny blocked_sites http_access deny all3.3 客户端配置与验证
浏览器或操作系统把 HTTP 出口指向192.168.10.10:3128后,浏览器发往目标站点的请求就会先到 squid。验证是否生效,最直接的方法是查看 access.log:
tail -f /var/log/squid/access.log当你在浏览器里打开一个网页,日志中会多出类似这样的记录:
1690000000.000 123 192.168.10.5 TCP_MISS/200 1024 GET http://www.example.com/看到TCP_MISS表示首次访问,还没命中缓存;再刷新一次浏览器,日志里会变成TCP_HIT,说明本地缓存已经建立。连续刷三次同样的页面,只有第一条是 MISS,后面都应该是 HIT,这就是缓存生效的铁证。
3.4 我踩过的一个坑:缓存目录权限
有一次配置完以后,squid 进程正常启动,但日志疯狂报警,提示缓存目录无法写入。排查了半天发现是因为我用了/data/squid/cache这个自定义目录,但没把该目录的所有者改成运行 squid 的系统用户。解决方式很简单:
chown -R squid:squid /data/squid/cache养成习惯:创建完缓存目录后立刻检查属主,再执行squid -z初始化,最后再启动服务。
4. 反向接入模式配置实操:给 Web 集群加一层入口守护
4.1 反向接入的整体思路
反向接入模式下,squid 对外表现为一个普通 Web 入口,监听 80 或 443 端口。它接收所有访问请求,然后按照规则把请求转发给内网的真实业务服务器。整个过程对用户透明,用户只会认为 squid 的地址就是网站地址。
这里需要重点理解的是cache_peer指令。它用来定义后端源站,可以指定多个源站,squid 会按照轮询方式把请求分发下去。同时配合cache_peer_domain或cache_peer_access可以把不同域名转发到不同的后端组,实现简单的分流。
4.2 反向接入模式核心配置
# 监听 80 端口,工作在加速模式 http_port 80 accel vhost # 关闭转发功能,不向外网直接抓取 # 这里通过 never_direct 强制所有请求都走 cache_peer never_direct allow all # 定义后端源站 cache_peer 192.168.30.11 parent 8080 0 no-query originserver name=web1 cache_peer 192.168.30.12 parent 8080 0 no-query originserver name=web2 # 将 www.example.com 的请求转发给这两个后端 cache_peer_domain web1 web2 .example.com www.example.com # 缓存规则:静态资源缓存 24 小时 acl static_assets urlpath_regex -i \.(css|js|png|jpg|jpeg|gif|webp|svg|woff2?)$ http_access allow static_assets # 动态请求不缓存 acl dynamic_urls urlpath_regex -i \.(php|do|action)$ http_access deny dynamic_urls注意accel vhost这两个参数是反向接入模式的关键。accel让 squid 进入加速模式,不再要求请求里必须有完整的转发链;vhost允许 squid 根据请求中的 Host 头来决定后端目标,而不是把配置写死成单台服务器。如果你只加了一个后端,可以省掉vhost,但做多站点托管时一定要保留。
4.3 缓存规则制定的经验
反向接入中最容易犯的错误是把所有页面全都缓存。动态接口一旦被缓存,用户会看到过期的订单状态、陈旧的库存信息,甚至登录失败。我的经验是:把缓存优先级放在静态资源上,动态 URL 一律透传。
squid 判断动态请求通常靠 URL 后缀或规则匹配。上面配置里dynamic_urls那一行已经拒绝了动态后缀,但有些网站的路由是伪静态的,比如/product/id/123,这时候再用后缀匹配就不明智。可以考虑用acl dynamic_urls urlpath_regex -i /api/这样的关键字匹配,或者干脆依赖后端返回的Cache-Control响应头。
更好的做法是让后端在响应头里显式声明缓存策略,然后 squid 通过refresh_pattern配合响应头来决定缓存时长。这样前后端职责分明,缓存策略不容易出错。
4.4 后端健康检查与宕机转移
cache_peer自身带有简单的存活检测,但默认并不频繁。我建议在配置里加上监测周期和连续失败次数:
cache_peer 192.168.30.11 parent 8080 0 no-query originserver monitorurl=/healthz connect-fail-limit=2 allow-missconnect-fail-limit=2表示连续两次连接失败就标记该源站为不可用,后续请求自动转移到其他源站。这里的allow-miss也很重要,它允许 squid 在上游异常时依然返回本地缓存副本。否则一旦源站挂了,所有本来能命中缓存的静态资源也会变得不可用。
5. 透明接管模式配置实操:网络层引流与免配置接入
5.1 为什么需要透明接管
正向模式要求每台客户端手动指定出口地址,这在小型办公室还能接受,但在几百人的教室、宿舍、园区网里几乎是不可行的。透明接管模式让 squid 成为路径上的一个“隐形关卡”,客户端不需要任何修改。
它依赖网络层的重定向,最常见的做法是用 iptables 把发往 80 端口的 TCP 包重定向到 squid 的一个监听端口。当然,iptables 重定向的能力通常是本机范围内的,如果是跨设备引流,则需要路由器或交换机上配合策略路由。本文以内网单机部署为例,讲解最常见的 iptables 方案。
5.2 透明接管模式核心配置
squid 侧的配置和正向模式类似,但要加上transparent关键字:
# 监听 3128,以透明模式工作 http_port 3128 transparent # 缓存目录和日志 cache_dir ufs /data/squid/cache 10240 16 256 access_log /var/log/squid/access.log squid # 允许内网所有网段 acl all_net src 0.0.0.0/0 http_access allow all_net http_access deny all网络侧的 iptables 规则一般放在 nat 表的 PREROUTING 链:
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3128这条规则的意思是:从内网网卡 eth0 进入、目标端口是 80 的 TCP 包,全部重定向到本机 3128 端口。squid 通过透明模式识别出原始目标地址,主动发起连接,再把响应返回给客户端。用户在浏览器里输入网址时,地址栏不变,请求先经过 squid,但客户端自己完全没有感知。
注意,这里只重定向了 HTTP 的 80 端口。如果还想接管 HTTPS(443 端口),情况要复杂得多,因为 TLS 握手阶段 squid 必须知道目标域名,需要用 SSL 拦截或显式配置,我不建议新手直接在生产环境开干,可以先从 HTTP 场景入手。
5.3 透明接管模式的路由环回坑
透明接管最常见的故障是“路由环回”。如果不加任何额外规则,重定向后的数据包可能又被 squid 本身的网络栈重新判断为发往外部的流量,从而再次进入 PREROUTING,产生死循环。
解决办法是在重定向之前排除由 squid 自己发出的流量:
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3128 iptables -t nat -A OUTPUT -o eth1 -p tcp --dport 80 -j REDIRECT --to-port 3128其实还有更稳妥的做法:在 squid 所在机器上关闭对 80 端口的内核转发,并把http_port监听地址绑定到内网 IP,这样避免对外转发路径和透明入口路径互相干扰。我这边的经验是,先把规则拆成最小验证,确认日志中出现请求后,再逐步放开其他链路。
5.4 与正向模式同时部署的思路
有些网络环境需要同时提供“主动配置型”和“透明接管型”两种接入方式。squid 允许在一个进程里监听多个端口,每个端口可以有不同的模式,比如:
http_port 3128 transparent http_port 3129这样一来,走网络层引流到 3128 的请求是透明的,而愿意手动配置的客户端可以使用 3129。这种混合部署在过渡期非常实用,不用担心切换窗口期客户端全都无法上网。
6. 常见故障与调优经验:手把手排查实录
6.1 访问被拒绝:多半是规则顺序问题
症状是客户端访问任意地址都返回 403,日志里出现TCP_DENIED。这时候先去检查http_access规则的顺序。squid 从上往下匹配,匹配即生效。如果deny all写在allow前面,所有请求都会被拒绝。
还有一种隐蔽情况:客户端 IP 不在任何acl src网段内。比如内网实际是 172.16.0.0/16,但你在 acl 里只写了 192.168.10.0/24,那当然匹配不上。建议先用print或日志里记录的源 IP 反向确认,别想当然。
6.2 缓存命中率低:检查对象尺寸和 refresh_pattern
有时候日志里全是TCP_MISS,命中率不到 10%。原因通常是默认的minimum_object_size或maximum_object_size设置过小。小于 0KB 的对象不会被缓存,如果站点大量图片都是 5KB 以内,而最小值被设成 8KB,那这些图片永远不会进缓存。
另外refresh_pattern也要关注。不同后缀的资源刷新周期差别很大,图片、CSS、JS 可以给 24 小时甚至 7 天,但 HTML 页面最好用较短时间。我常用的模板如下:
refresh_pattern -i \.(css|js|png|jpg|jpeg|gif|webp|svg|ico)$ 1440 80% 2880 refresh_pattern -i \.(html|htm|php|asp|aspx)$ 0 50% 60 refresh_pattern -i . 5 40% 4320最后一行默认规则很重要,它会兜住所有没有匹配上的 URL,防止因规则遗漏导致缓存策略混乱。
6.3 高并发时文件描述符不足
当并发请求数超过系统默认限制,squid 会在 cache.log 中报too many open files。处理办法是提高进程可打开的文件数限制:
ulimit -n 65535同时检查/etc/security/limits.conf,给运行 squid 的系统用户设置更高的软硬限制。如果设备内存充足,还可以适当调大cache_mem,把热门对象留在内存里,减少磁盘读取,这是提升并发响应速度最直接的手段。
6.4 DNS 解析导致首次访问极慢
squid 自己需要一个运行良好的 DNS 解析环境。如果上游 DNS 延迟高或者不稳定,首次访问新域名时会出现长时间卡住,而后台日志里没有任何异常。这种情况可以在配置里增加dns_nameservers指定更快的 DNS 地址:
dns_nameservers 223.5.5.5 8.8.8.8这里注意别同时把太多 DNS 写上,两个就够。另外可以开启positive_dns_ttl,对解析结果做一段时间的缓存,减少重复解析带来的等待。
6.5 磁盘空间增长过快
有些环境里缓存目录蹭蹭涨,很快把磁盘填满。这不一定是 bug,而是没有设好cache_dir的上限以及maximum_object_size上限。如果站点里存在超大视频文件,squid 默认也可能去缓存,很快就会吃掉大量空间。我建议把超过 64MB 的对象直接跳过缓存:
maximum_object_size 64 MB minimum_object_size 0 KB同时写一个磁盘空间监控脚本,当缓存目录超过总大小 80% 时自动清理最老的缓存文件,防止业务磁盘被写满。
6.6 上线前必做的四件事
这里是我每次部署 squid 都会走一遍的检查清单,直接用即可:
- 执行
squid -k parse验证配置文件语法,有错误会直接指出行号 - 执行
squid -z初始化缓存目录,确认没有权限错误 - 启动后看
/var/log/squid/cache.log,确保没有 FATAL 级别日志 - 从客户端发起一次真实请求,核对 access.log 中的响应码和命中状态
做完这四步,一个可用的转发缓存服务就已经跑起来了。后续再逐步叠加访问控制、多后端接入、透明引流这些高级特性。
写在最后的一点个人心得
squid 这玩意儿,虽然老,但它的配置模型非常经典。很多人一开始被各种 ACL、refresh_pattern 绕得晕头转向,其实只要记住“先定义对象、再定义规则、最后控制动作”这条主线,后面所有配置都是往这个框架里填内容。我在实际项目里观察下来,真正容易出问题的地方往往不在 squid 本身,而是周边配套——防火墙规则、路由策略、DNS 解析、磁盘空间。把这些环境因素管好,squid 能跑得非常扎实。
如果后续你想继续扩展,可以试试在反向接入模式下给不同域名配置不同的后端节点组,或者把透明接管模式扩展到 IPv6 场景。另外,squid 的日志字段非常丰富,接上开源日志采集工具后,完全可以做出企业内部出口流量的可视化分析看板。希望这篇配置解析能帮你在实际业务里少踩几个坑,把这台经典服务真正用起来。