Squid Debug Logging: Turn It On and Read It
Learn how to enable Squid debug_options, navigate cache.log and access.log, and trace the exact cause of a denied or failed proxy request.
Before you start
- ▸Squid 5 or 6 installed and running as a systemd service
- ▸Root or sudo access to edit /etc/squid/squid.conf and read log files
- ▸A reproducible failing request to test against
- ▸Sufficient free disk space (at least 2 GB) before raising debug levels
Squid's default logging tells you what happened; debug logging tells you why. When a client gets an unexpected TCP_DENIED, a cache miss makes no sense, or an SSL bump handshake silently fails, the answer is almost always buried in cache.log at a higher verbosity level. This guide walks through enabling debug sections, reading the output without going blind, and tracing a failed request from symptom to root cause.
How Squid Debug Logging Works
Squid organises its source code into numbered sections, each covering a subsystem (ACLs, DNS, SSL, disk cache, and so on). The debug_options directive in squid.conf controls which sections log and at what verbosity level (1–9, where 9 is maximally verbose). The syntax is:
debug_options section,level [section,level ...]
The special token ALL applies a level to every section. ALL,1 is the compiled-in default — only critical events. ALL,9 floods the disk; use it only briefly and on a low-traffic proxy.
Key Debug Sections
| Section | Subsystem | When to raise it |
|---|---|---|
| 5 | Disk I/O / store | Cache read/write failures |
| 11 | HTTP client side | Malformed requests, keep-alive issues |
| 14 | URL parser / hostname | Odd domain handling |
| 20 | ACL matching | Unexpected DENY or ALLOW results |
| 28 | Access control (http_access) | Policy evaluation trace |
| 33 | DNS / FQDN lookups | Slow or failing name resolution |
| 44 | Peer selection / hierarchy | Cache peer routing failures |
| 83 | SSL / TLS (bump) | Certificate errors, handshake failures |
A complete list ships with every Squid release; look for src/debug.cc or run squid -v to find the build path, then consult the official Squid debug sections wiki page.
Step 1 — Edit squid.conf
Open the main configuration file. The default path varies slightly:
- Debian/Ubuntu:
/etc/squid/squid.conf - Fedora/RHEL/Rocky:
/etc/squid/squid.conf - Arch:
/etc/squid/squid.conf
Find any existing debug_options line (or add one near the top of the file). To debug ACL denials and SSL issues simultaneously without nuking the whole log:
debug_options ALL,1 20,5 28,5 83,5
This keeps everything else at level 1 while raising ACL matching, access control, and SSL to level 5. For a complete trace of a single subsystem during a controlled test, go to level 9:
debug_options ALL,1 20,9 28,9
Never leave level 9 active in production. A busy proxy can write gigabytes per minute to cache.log and saturate the disk.
Step 2 — Restart or Reload Squid
A full restart is the safest way to activate a new debug_options line because some settings are not re-read on a reload:
sudo systemctl restart squid
On RHEL/Rocky the service may be named squid or, on older installs, require the explicit unit:
sudo systemctl restart squid.service
Confirm Squid came back up:
systemctl status squid
Step 3 — Reproduce the Problem and Capture the Log
Tail cache.log in one terminal while triggering the failing request from the client in another. The log path is set by cache_log in squid.conf; the default is:
sudo tail -f /var/log/squid/cache.log
On Arch, Squid may write to /var/log/squid4/cache.log depending on the AUR package build.
To save a timestamped snapshot for later analysis rather than reading live:
sudo tail -n 5000 /var/log/squid/cache.log > /tmp/squid-debug-$(date +%Y%m%d-%H%M%S).log
Step 4 — Understand the cache.log Format
Each debug line follows this pattern:
YYYY/MM/DD HH:MM:SS.mmm kid1| section,level: message
A real (shortened) example from an ACL denial at level 5:
2024/05/10 14:32:01.847 kid1| 28,5: ACLChecklist::matchAndFinish: aclname=blocked_domains result=1 (implicit DENY)
2024/05/10 14:32:01.848 kid1| 20,5: ACL::matches: 'blocked_domains' for example.evil: MATCH
The kid1 token is Squid's SMP worker identifier. On a single-worker setup you will only see kid1; SMP deployments add kid2, kid3, and so on. Filter to a single worker with grep when needed.
Step 5 — Read access.log Alongside cache.log
The access.log records the outcome of every request in a structured format. Cross-reference it with cache.log by timestamp to understand context. The default native log format is:
timestamp duration client result/status bytes method url user hierarchy type
Example line showing a denied CONNECT:
1715348021.847 1 192.168.1.55 TCP_DENIED/403 4043 CONNECT api.example.com:443 - HIER_NONE/- text/html
The result field format is cache-result/HTTP-status. Common result codes to know:
- TCP_DENIED — blocked by an ACL before Squid attempted a connection
- TCP_MISS — cache miss, fetched from origin
- TCP_MEM_HIT — served directly from RAM cache
- HIER_NONE — no upstream peer; Squid either denied or served itself
When access.log shows TCP_DENIED, pivot to cache.log at the same second and search for the client IP to find the ACL trace.
Step 6 — Trace a Failed Request End-to-End
Use grep with the client IP and a narrow time window:
grep '192.168.1.55' /var/log/squid/cache.log | grep '14:32:0'
For SSL bump failures, the relevant section is 83. Look for lines mentioning the server hostname and phrases like ssl error, certificate verify failed, or handshake:
grep -E '83,[0-9]|ssl_error|certificate' /var/log/squid/cache.log | tail -50
For DNS-related failures, section 33 at level 5 will show whether Squid received NXDOMAIN, timed out, or returned a stale cached answer:
grep '33,' /var/log/squid/cache.log | grep -i 'fail\|timeout\|nxdomain'
Step 7 — Turn Debug Logging Off
Once you have your answer, restore the default immediately. Edit squid.conf to remove or comment out the elevated debug_options line, then restart:
sudo systemctl restart squid
Rotate the log to start clean and reclaim disk space:
sudo squid -k rotate
Verification
Confirm that Squid is running cleanly and that debug output is no longer flooding the log:
systemctl status squid
sudo wc -l /var/log/squid/cache.log
After a restart with ALL,1, a quiet proxy should add only a handful of lines per minute to cache.log. If the count is climbing fast, debug_options is still elevated.
Troubleshooting
cache.log is empty or not updating
Check that cache_log in squid.conf points to a writable path and that the Squid process owns it. Run sudo squid -N -d1 in the foreground to verify startup errors before they are swallowed by systemd.
Log file grows to tens of GB in minutes
You have a high-traffic proxy with a section at level 9. Drop back to level 5 or narrow the section list. Consider redirecting debug output to a ramdisk (tmpfs) during testing if disk I/O is a constraint.
ACL trace shows ALLOW but request still fails
The denial may be at the peer or origin server, not in Squid itself. Check access.log for HIER_DIRECT vs HIER_NONE and raise section 11 (HTTP client side) to level 5 to inspect the full request/response exchange.
debug_options line is ignored after reload
Some Squid builds require a full restart (not just squid -k reconfigure) for debug_options to take effect. Always use systemctl restart squid when changing this directive.
Frequently asked questions
- What is the difference between debug level 5 and level 9?
- Level 5 logs significant decision points such as ACL match results and connection state changes. Level 9 logs every internal function call in the section — useful for deep bugs but extremely verbose; on a busy proxy it can write gigabytes per minute.
- Can I enable debug logging without restarting Squid?
- You can send SIGUSR2 to toggle debug output in some builds, and 'squid -k reconfigure' re-reads most directives. However, debug_options is not reliably applied without a full restart, so systemctl restart is recommended.
- How do I find the section number for a specific Squid feature?
- The Squid project maintains a list at https://wiki.squid-cache.org/KnowledgeBase/DebugSections. You can also grep the Squid source (debugs(XX, Y, ...)) for the file corresponding to the feature you are investigating.
- Why does access.log show TCP_DENIED/403 but cache.log shows nothing useful?
- Your debug_options level is probably still at ALL,1. Raise sections 20 and 28 to level 5 and reproduce the request; the ACL evaluation trace will then appear in cache.log with matching timestamps.
- Is it safe to use debug_options in production?
- Briefly, yes — using a targeted section at level 5 on a low- to medium-traffic proxy causes acceptable overhead. Avoid ALL,9 in production entirely; it creates a serious disk and performance risk within minutes on any reasonably loaded system.
Related guides
Configure Prometheus Alertmanager
Configure Prometheus Alertmanager with routing trees, receivers, inhibition rules, grouping, Go templates, and PagerDuty/Slack on-call integrations.
Build an Intranet Server on Linux
Set up a complete small-office intranet on one Linux box: Nginx web server, dnsmasq local DNS, Samba file sharing, and a Wiki.js team wiki.
Build an nftables Firewall Script
Build a complete nftables firewall from scratch: tables, chains, sets, default-deny input policy, service allowlisting, and persistent systemd configuration.
Caddy as a Reverse Proxy
Set up Caddy as a reverse proxy with automatic HTTPS, load balancing, WebSocket passthrough, reusable snippets, and header control — no certbot required.