Interception (transparent) proxying with Squid

Updated November 2026

Interception (often called transparent proxying) sends web traffic to Squid without configuring any browser: the router or firewall quietly redirects port 80 to the proxy. It works for plain HTTP, breaks some things you may not expect, and does not work for HTTPS unless you decrypt traffic, which raises serious legal and ethical questions. A proxy auto-config file is often the better choice.

How interception works

With a normal (explicit) proxy, the browser knows it is talking to Squid and sends full URLs. With interception, the client thinks it is talking straight to the web server. A firewall rule changes the packet’s path so it lands on a special Squid port instead. Squid then works out where the client meant to go and fetches the page on its behalf.

The Squid wiki is blunt about the costs:

The wiki also says to test in a non-live environment before you switch it on for users.

intercept versus tproxy

intercepttproxy
MechanismNAT (REDIRECT or DNAT)Linux TPROXY, BSD divert-to
Address servers seeThe proxy’s addressThe client’s own address
IPv6IPv4 only on older kernelsIPv4 and IPv6
Where it runsNAT rules must be on the Squid machineSquid box with policy routing

Use intercept for a simple setup where Squid sits in the traffic path. Use tproxy when servers or logs downstream must see real client addresses. Either way, keep an ordinary forward-proxy port as well; the Squid examples always pair them.

NAT interception (REDIRECT)

This is the Squid wiki’s REDIRECT example, for a Squid box at 192.168.0.2 that is also the gateway. In squid.conf:

http_port 3128
http_port 3129 intercept

Then the firewall rules (iptables):

SQUIDIP=192.168.0.2
SQUIDPORT=3129

iptables -t nat -A PREROUTING -s $SQUIDIP -p tcp --dport 80 -j ACCEPT
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port $SQUIDPORT
iptables -t nat -A POSTROUTING -j MASQUERADE
iptables -t mangle -A PREROUTING -p tcp --dport $SQUIDPORT -j DROP

Line by line: Squid’s own outgoing requests are left alone (without this rule you get a forwarding loop); every other port-80 connection is redirected to port 3129; outgoing traffic is masqueraded; and direct connections to port 3129 from outside are dropped, because the mangle table runs before NAT. If the router and Squid are different machines, the wiki says to use policy routing to deliver packets to the Squid box, and do the NAT there.

The Squid wiki gives iptables rules only. The same REDIRECT setup written for nftables, using the syntax in the nft manual, looks like this:

table ip nat {
    chain prerouting {
        type nat hook prerouting priority dstnat; policy accept;
        ip saddr 192.168.0.2 tcp dport 80 accept
        tcp dport 80 redirect to :3129
    }
    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        masquerade
    }
}
table inet filter {
    chain prerouting {
        type filter hook prerouting priority mangle; policy accept;
        tcp dport 3129 drop
    }
}

Save it as a file and load it with nft -f. Test it on a lab machine first; on a live gateway, a mistake in the NAT table cuts everyone off.

TPROXY interception

TPROXY keeps the client’s address on outgoing connections. Squid must be built with --enable-linux-netfilter (distribution packages normally are), and the port must be dedicated to tproxy:

http_port 3128
http_port 3129 tproxy

The wiki’s packet-marking and routing rules:

iptables -t mangle -N DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
iptables -t mangle -A DIVERT -j ACCEPT
iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 3129

ip -f inet rule add fwmark 1 lookup 100
ip -f inet route add local default dev eth0 table 100

The DIVERT rule must come before the TPROXY rule. You also need IP forwarding on and reverse-path filtering off (net.ipv4.ip_forward = 1, net.ipv4.conf.all.rp_filter = 0 and the same for the interface). For IPv6, repeat the rules with ip6tables and ip -f inet6.

Why HTTPS is different

An HTTPS connection is encrypted from the browser to the server, and the server proves its identity with a certificate. A proxy in the middle cannot read the request, so redirecting port 443 to a normal intercept port does not work.

Squid’s SSL-Bump feature can read the TLS handshake and decide what to do in steps. In the first steps it can peek at the server name the client asked for (SNI) without changing anything, then splice (pass the connection through untouched) or bump (decrypt and re-encrypt). The least intrusive setup only peeks and splices, which lets you log or filter by host name without decrypting anything:

ssl_bump peek all
ssl_bump splice all

This still needs an https_port with intercept ssl-bump and a certificate (tls-cert=), and a Squid built with OpenSSL (on Debian, the squid-openssl package). Bumping goes further: Squid generates a fake certificate for each site, signed by your own certificate authority, which must be installed as trusted on every client. Apps that pin their certificates will refuse to connect at all.

Legal and ethical limits. Decrypting other people’s HTTPS exposes passwords, banking, health and private messages. Intercepting communications without authority or consent is a crime in many countries; in the United States, for example, the federal Wiretap Act (18 U.S.C. § 2511) bans intentional interception, with narrow exceptions such as consent and a service provider protecting its own service. If you plan it for a workplace, get a written policy, tell users, take legal advice first, and splice (do not decrypt) sensitive categories such as banking and health. If you are not sure you are allowed to, do not do it.

Alternatives: PAC files and WPAD

Often you do not need interception at all. A proxy auto-config (PAC) file is a small JavaScript function the browser runs for every request to choose a proxy:

function FindProxyForURL(url, host) {
  if (isPlainHostName(host) || dnsDomainIs(host, ".example.lan")) {
    return "DIRECT";
  }
  return "PROXY proxy.example.lan:3128; DIRECT";
}

Serve it with the MIME type application/x-ns-proxy-autoconfig. The browser then uses Squid as an explicit proxy, so authentication works, HTTPS goes through CONNECT normally and nothing breaks when the proxy is down (the ; DIRECT fallback).

WPAD (Web Proxy Auto-Discovery) lets browsers find the PAC file on their own, through DHCP option 252 or a DNS name such as wpad.example.lan, fetching /wpad.dat. It never became a standard: the IETF draft expired in 1999. Any machine that manages to answer to the name “wpad” can steer browsers through itself, so make sure only your own server can claim that name, or set the PAC address directly through group policy or device management.

Sources