Context: When Does a Business Need Squid Proxy?
I first got my hands on Squid back when I was a sysadmin at a logistics company. The IT team was getting complaints from HR that employees were watching YouTube during work hours, and the bandwidth drain was causing video calls to lag constantly. The solution was to set up a Squid Proxy — and it was done within a single morning.
Squid Proxy Server lets you do things a regular router simply cannot:
- Domain-based access control: Block YouTube, Facebook, TikTok by list, or whitelist only the sites that are actually needed
- Time-based control: Allow social media during lunch breaks, block it outside those hours
- Content caching: Multiple machines downloading the same file — only one trip out to the Internet, saving significant bandwidth
- Full audit logging: Know exactly which machine, which IP, visited which website, and at what time
- Internal IP masking: Clients reach the Internet through the proxy IP, keeping your internal network topology hidden
My company still has a few servers running CentOS 7, and migrating them to AlmaLinux is a problem I’ve already tackled. But for low-traffic proxy servers, the team still opts for CentOS Stream 9 due to its clear support lifecycle and solid compatibility with the RHEL ecosystem. In this article, I’ll walk through setting up Squid from scratch, including handling SELinux and firewalld — the two things that give junior developers the most headaches when first approaching RHEL/CentOS.
Installing Squid on CentOS Stream 9
Step 1: Update the System and Install Squid
Start by updating the system, then install Squid from the default CentOS Stream 9 repository:
sudo dnf update -y
sudo dnf install squid -y
Verify the installed version:
squid -v
The output returns build information, including a line showing Squid Cache: Version 5.x.
Step 2: Start and Enable the Service
sudo systemctl enable squid --now
sudo systemctl status squid
If you see Active: active (running), you’re good to go. Squid listens on port 3128 by default.
Detailed Squid Configuration
Creating a Clean Configuration File
The default /etc/squid/squid.conf file has around ~7000 lines of comments. I usually back it up and rewrite a leaner config file for easier management:
sudo cp /etc/squid/squid.conf /etc/squid/squid.conf.backup
sudo nano /etc/squid/squid.conf
Paste in a standard enterprise configuration:
# ============================================================
# Squid Proxy - Enterprise Config
# CentOS Stream 9
# ============================================================
# Listening port
http_port 3128
# === ACL: Define local network ===
acl localnet src 192.168.0.0/16
acl localnet src 10.0.0.0/8
acl localnet src 172.16.0.0/12
# Standard ACLs
acl SSL_ports port 443
acl Safe_ports port 80 443 8080 8443 21 70 210 280 488 591 777
acl CONNECT method CONNECT
# === ACL: Blocked domain list ===
acl blocked_sites dstdomain "/etc/squid/blocked_sites.txt"
# === Rules ===
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access deny blocked_sites
http_access allow localnet
http_access allow localhost
http_access deny all
# === Cache ===
cache_dir ufs /var/spool/squid 1024 16 256
maximum_object_size 50 MB
cache_mem 256 MB
# === Logging ===
access_log /var/log/squid/access.log squid
cache_log /var/log/squid/cache.log
# Hide server information
via off
forwarded_for off
httpd_suppress_version_string on
Creating the Blocked Website List
sudo nano /etc/squid/blocked_sites.txt
Example content:
.youtube.com
.facebook.com
.tiktok.com
.netflix.com
.instagram.com
Note: The leading dot . before a domain blocks all subdomains as well — for example, .youtube.com also blocks www.youtube.com, m.youtube.com, and music.youtube.com.
Configuring Time-Based ACLs for Working Hours
A real-world scenario: allow social media during the lunch break (12:00–13:00), block it outside that window:
# Add to squid.conf (before the Rules section)
# Working hours: Monday-Friday, 8am-12pm and 1pm-6pm
acl working_hours time MTWHF 08:00-12:00
acl working_hours time MTWHF 13:00-18:00
# Lunch break
acl lunch_break time MTWHF 12:00-13:00
# Social media domains
acl social_media dstdomain .facebook.com .youtube.com .tiktok.com
# Applied rules:
http_access deny social_media working_hours
http_access allow social_media lunch_break localnet
Configuring SELinux for Squid
This is the part many people skip, then wonder why Squid won’t run even though the config looks correct. SELinux on CentOS Stream 9 is in Enforcing mode by default.
Check the current status:
getenforce
# Output: Enforcing
Squid needs some specific SELinux permissions. Port 3128 is already allowed by SELinux by default, but if you want to switch to a different port (e.g., 8080):
# Add port 8080 to the squid_port_t policy
sudo semanage port -a -t squid_port_t -p tcp 8080
# Verify
sudo semanage port -l | grep squid
If Squid needs to connect to non-standard ports (e.g., an upstream proxy), enable this boolean:
sudo setsebool -P squid_connect_any 1
When you encounter a permission denied error and aren’t sure what SELinux is blocking, here’s a quick debugging approach:
# View recent audit log denials
sudo ausearch -c 'squid' --raw | audit2allow -M my-squid
# Apply the generated policy
sudo semodule -i my-squid.pp
Configuring firewalld
Open port 3128 so clients on the internal network can connect to the proxy:
# Use the built-in squid service in firewalld (more convenient)
sudo firewall-cmd --zone=internal --add-service=squid --permanent
# Or open the port directly if you prefer
sudo firewall-cmd --zone=internal --add-port=3128/tcp --permanent
# Apply changes
sudo firewall-cmd --reload
# Check the internal zone
sudo firewall-cmd --zone=internal --list-all
Check which zone your internal network interface belongs to, and move it to the internal zone if needed:
# See which interface is in which zone
sudo firewall-cmd --get-active-zones
# Example: eth1 is the internal interface
sudo firewall-cmd --zone=internal --add-interface=eth1 --permanent
sudo firewall-cmd --reload
Validate Config and Restart Squid
Always check the syntax before restarting — a bad config can prevent the service from starting at all:
# Parse and validate the config
sudo squid -k parse
# No errors → restart
sudo systemctl restart squid
Testing and Monitoring
Test the Proxy from a Client
From a client machine on the internal network, use curl to test through the proxy (replace 192.168.1.100 with the actual IP of your Squid server):
# Test normal access
curl -x http://192.168.1.100:3128 http://example.com -I
# Expected: HTTP/1.1 200 OK
# Test blocked domain
curl -x http://192.168.1.100:3128 http://www.youtube.com -I
# Expected: HTTP/1.1 403 Forbidden
View Logs in Real Time
Squid’s access log records all activity — this is the primary source of information for security auditing:
sudo tail -f /var/log/squid/access.log
Each log line looks like this:
1720123456.789 1234 192.168.1.50 TCP_MISS/200 5432 GET http://example.com/ - DIRECT/93.184.216.34 text/html
- 1720123456.789: Unix timestamp
- 1234: Processing time (milliseconds)
- 192.168.1.50: Client IP
- TCP_MISS/200: Cache miss, server returned HTTP 200
- GET http://example.com/: HTTP method and full URL
View Squid Statistics
# General overview statistics
sudo squidclient -h localhost mgr:info
# Cache hit rate
sudo squidclient -h localhost mgr:counters | grep -i hit
Script to Find the Top Most-Accessed Domains
Useful when you need to generate weekly reports for management:
#!/bin/bash
# Top 10 most accessed domains in the current log
cat /var/log/squid/access.log | \
awk '{print $7}' | \
sed 's|http[s]*://||' | \
cut -d'/' -f1 | \
sort | uniq -c | \
sort -rn | \
head -10
Configuring Log Rotation
In a high-traffic environment, Squid logs can balloon to several gigabytes within days. Configure logrotate to clean them up automatically:
sudo nano /etc/logrotate.d/squid
/var/log/squid/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
postrotate
/usr/sbin/squid -k rotate
endscript
}
Troubleshooting Common Issues
Squid won’t start — permission error:
# Check if SELinux is blocking anything
sudo ausearch -m avc -ts recent | grep squid
# Quick debug: temporarily set permissive mode (for testing ONLY, never use in production)
sudo setenforce 0
sudo systemctl restart squid
# If it works → the issue is SELinux → use audit2allow to fix it properly
sudo setenforce 1
Client cannot connect through the proxy:
# Which port is Squid listening on?
sudo ss -tlnp | grep squid
# Is the firewall blocking anything?
sudo firewall-cmd --list-all --zone=internal
# Try telnet from the client to the proxy
telnet 192.168.1.100 3128
Once Squid is up and running stably, the next step I usually take is switching to a transparent proxy — clients don’t need to configure a proxy manually, as all HTTP traffic is automatically routed through Squid via iptables redirect. But that’s a more complex topic that also requires SSL bump to inspect HTTPS traffic — I’ll save it for another article.

