Squid ACL Configuration Explained
Learn how Squid proxy ACL definitions and http_access rules work, why ordering is critical, and avoid the most common allow/deny configuration mistakes.
Before you start
- ▸Squid 5.x or 6.x installed and running (sudo systemctl status squid)
- ▸Root or sudo access to edit /etc/squid/squid.conf
- ▸Basic understanding of CIDR notation and TCP ports
- ▸NTP synchronisation active on the proxy server if using time ACLs
Squid's access control system is the mechanism behind almost every useful proxy policy: blocking sites, restricting hours, limiting which clients can use the proxy, and more. It is also the most common source of misconfiguration. Understanding how ACL definitions and http_access rules interact — and why order matters absolutely — will save you hours of debugging.
How ACLs and http_access Work Together
Squid access control is a two-layer system. First you define named ACLs; then you write http_access rules that reference those names to allow or deny requests.
- ACL definition — describes a condition: an IP range, a domain list, a time window, a port, etc.
- http_access rule — says
allowordenywhen one or more named ACLs all match a request simultaneously.
Squid evaluates http_access rules top to bottom. The first rule whose ACL conditions all match wins. If no rule matches, Squid applies the opposite of the last explicit rule's action — in practice this usually means deny, which is why every production config ends with http_access deny all.
ACL Syntax Reference
The general form is:
acl ACL_NAME type value [value ...]
Multiple lines with the same name and type are OR-combined. The most commonly used types are:
| Type | Matches | Example value |
|---|---|---|
| src | Client IP / CIDR | 192.168.1.0/24 |
| dst | Destination IP | 10.0.0.0/8 |
| dstdomain | Destination hostname | .example.com |
| url_regex | Full URL regex | -i \.exe$ |
| port | Destination TCP port | 443 8443 |
| time | Day/hour window | MTWHF 08:00-17:00 |
| proto | Protocol | HTTP HTTPS |
| method | HTTP method | CONNECT POST |
Step 1 — Locate and Back Up squid.conf
On most distributions Squid's main config is /etc/squid/squid.conf. Always back it up before editing.
sudo cp /etc/squid/squid.conf /etc/squid/squid.conf.bak
Step 2 — Define Source (Client) ACLs
The most basic policy starts by identifying which clients are trusted. Define ACLs for your internal networks before writing any http_access rules.
# Internal LAN
acl localnet src 192.168.0.0/16
acl localnet src 10.0.0.0/8
acl localnet src 172.16.0.0/12
# A specific admin workstation
acl admin_host src 192.168.1.10/32
Notice both localnet lines use the same name — Squid treats them as a single OR-combined ACL. A request from either range will match.
Step 3 — Define Destination and Content ACLs
Restrict what those clients can reach. Use dstdomain for hostname matching (the leading dot matches the domain and all subdomains), or load a list from a file for larger sets.
# Block social media domains
acl social_media dstdomain .facebook.com .twitter.com .tiktok.com .instagram.com
# Allowlist for finance team
acl finance_sites dstdomain .reuters.com .bloomberg.com .ft.com
# Block executable downloads by URL pattern
acl blocked_extensions url_regex -i \.(exe|msi|bat|cmd)$
# Load a large blocklist from a file (one domain per line)
acl blocklist dstdomain "/etc/squid/blocklist.txt"
The -i flag on url_regex makes matching case-insensitive. Regex ACLs are slower than domain ACLs on large lists — prefer dstdomain files wherever possible.
Step 4 — Define Time ACLs
Time ACLs restrict access to specific days and hours. Day codes are: S Sunday, M Monday, T Tuesday, W Wednesday, H Thursday, F Friday, A Saturday.
# Business hours, Monday through Friday
acl business_hours time MTWHF 08:00-17:30
# Lunch break
acl lunch_break time MTWHF 12:00-13:00
# Weekends
acl weekend time SA
Squid uses the proxy server's local clock, so make sure the system time is correct — run timedatectl status to confirm NTP synchronisation is active.
Step 5 — Write http_access Rules in the Correct Order
This is where most mistakes happen. Rules are evaluated strictly top-to-bottom; the first match stops evaluation. A common working pattern for a corporate proxy:
# Always allow localhost management
http_access allow localhost manager
http_access deny manager
# Block social media for everyone during business hours
http_access deny social_media business_hours
# Block executable downloads from all clients
http_access deny blocked_extensions
# Allow internal clients within business hours
http_access allow localnet business_hours
# Allow admin host at any time
http_access allow admin_host
# Deny everything else
http_access deny all
When a rule lists multiple ACL names on the same line, all of them must match — it is a logical AND. Separate http_access lines for the same action are OR-combined at the policy level because the first matching line wins.
Inline Multiple-ACL AND Logic
# Only allow finance sites to users in the finance VLAN during business hours
acl finance_vlan src 10.10.20.0/24
http_access allow finance_vlan finance_sites business_hours
All three ACLs must be true simultaneously for this rule to match.
Step 6 — Validate and Reload Squid
Always parse the config before reloading to catch syntax errors:
sudo squid -k parse
A clean parse produces no output. If there are errors, Squid reports the file and line number. Once clean, reload without dropping connections:
sudo systemctl reload squid
On Fedora/RHEL the service name may be squid on older packages or differ slightly — confirm with systemctl status squid.
Installing Squid (if not already present)
# Debian / Ubuntu
sudo apt install squid
# Fedora / RHEL 9+ / Rocky
sudo dnf install squid
# Arch
sudo pacman -S squid
Verification
Test that your rules behave as expected from a client machine. With curl pointed at the proxy:
# Should succeed (allowed site)
curl -x http://proxy.example.com:3128 https://www.reuters.com -I
# Should return 403 or TCP RESET (blocked site)
curl -x http://proxy.example.com:3128 https://www.facebook.com -I
Watch the access log in real time to confirm which ACL is matching:
sudo tail -f /var/log/squid/access.log
The log format includes the result code (TCP_DENIED, TCP_MISS, etc.) and the client IP, giving you direct confirmation of what is being allowed or denied and from where.
Common Mistakes and Troubleshooting
Everything is blocked even for trusted clients
You almost certainly have http_access deny all appearing before your allow rules. Check the order — deny-all must be the last line in your http_access block, not the first.
Blocklist is not working
If you used a file path like "/etc/squid/blocklist.txt", verify the file exists and is readable by the squid (or proxy) user:
sudo -u squid cat /etc/squid/blocklist.txt | head -5
Also make sure entries do not include http:// prefixes — dstdomain files expect bare hostnames like .example.com.
HTTPS sites are not being blocked
For HTTPS, Squid only sees the CONNECT tunnel hostname, not the full URL. dstdomain ACLs still work for HTTPS because they match against the CONNECT request target. However, url_regex ACLs matching URL paths will not work for HTTPS without SSL Bump configured — that is a separate, more complex feature.
Time ACL is matching at wrong hours
Squid uses server local time. If the proxy is in UTC and users are in another timezone, the hours in your time ACL must be expressed in server local time, not user local time. Verify with:
timedatectl
date
squid -k parse reports "(none)"
A parse warning reading ACL name defined but not used is harmless. An error reading aclParseAclLine: Invalid ACL type means a typo in the ACL type keyword — check spelling and Squid version compatibility.
Frequently asked questions
- Why does Squid block everything even though I wrote 'http_access allow localnet'?
- Almost always the 'http_access deny all' line appears before your allow rule. Squid stops at the first match, so deny-all at the top blocks everything. Move it to the very last line in your http_access block.
- What is the difference between putting two ACL names on one http_access line versus using two separate http_access lines?
- Two names on the same line require both to match simultaneously (logical AND). Two separate http_access lines each independently try to match in order (effectively OR, because the first matching line wins).
- Can I block HTTPS sites with dstdomain ACLs without SSL Bump?
- Yes. For HTTPS traffic Squid receives a CONNECT request naming the target hostname, and dstdomain matches against that hostname. You cannot inspect URL paths or content without SSL Bump, but domain-level blocking works fine.
- How do I apply a block only during certain hours and allow it outside those hours?
- Define a time ACL for the restricted window, then write 'http_access deny blocked_acl time_acl' before your general allow rule. Outside the time window the time ACL will not match, so the deny rule is skipped and the normal allow rule applies.
- Are url_regex ACLs slow? Should I avoid them?
- url_regex requires Squid to run a regular expression against every request URL, which is significantly slower than dstdomain lookups on large lists. Use dstdomain file-based lists for domain blocking and reserve url_regex for cases where you genuinely need pattern matching, like file extensions.
Related guides
Build a Mesh VPN with Nebula
Build a fully self-hosted mesh VPN with Nebula: create a CA, sign node certs, configure lighthouses, enforce group-based firewall rules, and run as a systemd service.
Common Linux Network Ports Reference
Learn Linux port ranges, read /etc/services, find what's listening with ss and nmap, and apply solid firewall rules to expose or block the right ports.
How to Configure a Static IP on Linux
Configure a static IP on Linux using Netplan, NetworkManager (nmcli), or systemd-networkd across Ubuntu, Fedora, Debian, and Arch with verified steps.
Expose a Service with Cloudflare Tunnel
Expose local services to the internet without port-forwarding using Cloudflare Tunnel. Install cloudflared, create a named tunnel, configure ingress rules, and run as a systemd service.