Software Engineering Wiki

Proxies and routing

Squid

ACL evaluation order, caching behaviour, authentication, TLS handling and log analysis for a forward proxy.

Cheatsheet #

TaskCommand
Check config before reloadsquid -k parse
Reload without dropping sessionssquid -k reconfigure
Rotate logssquid -k rotate
Runtime statisticssquidclient mgr:info
Current connectionssquidclient mgr:active_requests
Cache hit ratiosquidclient mgr:info | grep -i 'hit ratio'
Tail decisionstail -f /var/log/squid/access.log
Test a URL through the proxycurl -x localhost:3128 -sI https://example.com
Why was it deniedgrep TCP_DENIED /var/log/squid/access.log | tail
Initialise cache directoriessquid -z
Purge one objectsquidclient -m PURGE http://example.com/file
Which ACL matcheddebug_options ALL,1 33,2 28,9 then read cache.log

How a request is decided #

Squid walks http_access rules top to bottom and stops at the first match; the action of that rule wins. An allow after a matching deny never runs. Each rule is a set of ACL names ANDed together, and multiple lines are ORed.

acl localnet src 10.0.0.0/8
acl SSL_ports port 443
acl Safe_ports port 80 443 21 70 210 1025-65535
acl CONNECT method CONNECT

http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localnet
http_access deny all              # keep this last; the implicit default is the opposite of the last rule

The implicit final rule is the negation of the last explicit one, which is a reliable way to be surprised — always end with http_access deny all.

squid -k parse                                  # syntax and ACL sanity
grep -E 'TCP_DENIED|TAG_NONE' /var/log/squid/access.log | tail -20
curl -x localhost:3128 -sI https://example.com  # end-to-end check

ACL types #

TypeMatchesExample
src / dstClient / server addressacl office src 10.1.0.0/16
dstdomainDestination domainacl allowed dstdomain .example.com
dstdom_regexDomain regexacl bad dstdom_regex -i ads?\.
url_regexFull URL regexacl media url_regex -i \.(mp4|iso)$
portDestination portacl SSL_ports port 443
methodHTTP methodacl CONNECT method CONNECT
timeDay and houracl work time MTWHF 08:00-18:00
proxy_authAuthenticated useracl users proxy_auth REQUIRED
maxconnConcurrent connections per clientacl heavy maxconn 20
req_mime_typeRequest content typeacl upload req_mime_type -i multipart/form-data

A leading dot in dstdomain matches the domain and all subdomains; without it the match is exact. Keep long lists in files: acl allowed dstdomain "/etc/squid/allowed.txt".

Minimal working configuration #

http_port 3128
visible_hostname proxy.example.internal

acl localnet src 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
acl SSL_ports port 443
acl Safe_ports port 80 443
acl CONNECT method CONNECT

http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access allow localnet
http_access deny all

cache_dir ufs /var/spool/squid 10000 16 256
maximum_object_size 512 MB
cache_mem 512 MB
coredump_dir /var/spool/squid

access_log daemon:/var/log/squid/access.log squid
logfile_rotate 7

forwarded_for delete            # do not leak internal client addresses upstream
via off
dns_v4_first on
squid -z                 # create cache directories, once
systemctl enable --now squid
squid -k reconfigure     # after every change

Caching #

Squid obeys origin cache headers. Cache-Control: no-store or private means nothing is cached; missing validators mean revalidation on every request. refresh_pattern only controls heuristics for responses without explicit freshness.

refresh_pattern -i \.(deb|rpm|tgz|whl)$ 10080 90% 43200 override-expire
refresh_pattern ^ftp:           1440  20%  10080
refresh_pattern .                  0   20%   4320
Log tagMeaning
TCP_HITServed from cache
TCP_MEM_HITServed from memory cache
TCP_REFRESH_UNMODIFIEDRevalidated, origin said unchanged
TCP_MISSFetched from origin
TCP_DENIEDBlocked by http_access
TCP_TUNNELCONNECT tunnel, contents never cacheable
squidclient mgr:info | grep -iE 'hit ratio|objects|Storage'
squidclient mgr:storedir
squidclient -m PURGE http://example.com/path/file

HTTPS traffic through CONNECT is an opaque tunnel: nothing is cached and only the hostname is visible. A caching proxy in front of package mirrors needs those mirrors to be reachable over plain HTTP, or SSL bumping, which is a separate decision.

Authentication #

auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/passwd
auth_param basic children 20
auth_param basic realm Corporate proxy
auth_param basic credentialsttl 2 hours

acl authenticated proxy_auth REQUIRED
http_access allow authenticated
htpasswd -c /etc/squid/passwd alice        # basic auth file

For directory-backed authentication use basic_ldap_auth or Kerberos (negotiate_kerberos_auth), which avoids passwords entirely on domain-joined clients. Basic authentication sends credentials on every request — acceptable only inside a trusted network or over TLS to the proxy.

Transparent and reverse modes #

# Intercept mode: traffic redirected by the router or iptables
http_port 3129 intercept

# Reverse proxy (accelerator)
http_port 80 accel defaultsite=app.example.com
cache_peer 10.0.0.5 parent 8080 0 no-query originserver name=app
acl our_sites dstdomain app.example.com
http_access allow our_sites
cache_peer_access app allow our_sites

Intercepting HTTPS requires either a client-trusted CA (SSL bump) or leaving CONNECT tunnels intact. Bumping decrypts user traffic — get written authorisation before deploying it, and exclude banking and health categories explicitly.

Upstream proxies #

cache_peer upstream.example.com parent 3128 0 no-query default
never_direct allow all              # force everything through the parent
acl internal dstdomain .internal.example.com
always_direct allow internal        # except these

never_direct plus always_direct for exceptions is the standard shape in a restricted network; getting the order wrong silently leaks direct connections.

Logs and monitoring #

logformat combined %>a %[ui %[un [%tl] "%rm %ru HTTP/%rv" %>Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh
access_log daemon:/var/log/squid/access.log combined
tail -f /var/log/squid/access.log
grep -c TCP_DENIED /var/log/squid/access.log
awk '{print $4}' /var/log/squid/access.log | sort | uniq -c | sort -rn | head   # result codes
squidclient mgr:info
squidclient mgr:5min | grep -E 'client_http|server_http'

Export to Prometheus with squid-exporter, alerting on denied ratio, file descriptor use and cache disk fullness. Running out of file descriptors presents as random connection failures under load.

Troubleshooting #

SymptomCheck
Everything deniedRule order; an early deny matched. Read TCP_DENIED lines
Works by IP, not by nameSquid’s own DNS: dns_nameservers, dns_v4_first
HTTPS sites fail, HTTP worksCONNECT denied by Safe_ports/SSL_ports
Slow first byteUpstream DNS or parent proxy latency, not cache
Too many open filesRaise LimitNOFILE in the systemd unit and max_filedescriptors
Cache never hitsOrigin sends no-store/private, or traffic is all HTTPS tunnels
Reconfigure had no effectsquid -k parse failed silently in the unit; check journalctl -u squid

Oneliners #

# Top destinations
awk '{print $7}' /var/log/squid/access.log | awk -F/ '{print $3}' | sort | uniq -c | sort -rn | head

# Top clients by request count
awk '{print $3}' /var/log/squid/access.log | sort | uniq -c | sort -rn | head

# Bytes served per client
awk '{b[$3]+=$5} END {for (c in b) printf "%12d %s\n", b[c], c}' /var/log/squid/access.log | sort -rn | head

# Denied requests with the URL
awk '$4 ~ /DENIED/ {print $3, $7}' /var/log/squid/access.log | sort | uniq -c | sort -rn | head

# Hit ratio from the log itself
awk '{if ($4 ~ /HIT/) h++; t++} END {printf "%.1f%% of %d\n", 100*h/t, t}' /var/log/squid/access.log

# Requests per minute
awk '{print strftime("%H:%M", $1)}' /var/log/squid/access.log | uniq -c | tail -20

# Verify a client can reach a site through the proxy
curl -x proxy.example.internal:3128 -o /dev/null -sw '%{http_code} %{time_total}\n' https://example.com

# Check which ACL blocked a URL
squidclient -h localhost -p 3128 mgr:config | grep -A2 http_access

# Confirm the parent proxy is being used
grep -c 'FIRSTUP_PARENT' /var/log/squid/access.log

# Watch cache disk usage
squidclient mgr:storedir | grep -E 'Maximum|Current'

# Reload safely in a pipeline
squid -k parse && squid -k reconfigure

Last updated 15 September 2026 · Edit this page