Fundamentals

Transparent and intercepting proxies

How traffic is diverted without client configuration, what interception breaks (TLS, HSTS, pinning, IP auth), how to detect it, and TPROXY vs REDIRECT.

· 12 min read · How we verify this

Key points

  • Transparent and intercepting are not synonyms: RFC 2616 defines a transparent proxy as one that does not modify messages, while RFC 3040 defines interception as traffic diverted without client configuration.
  • Interception is structurally incompatible with 407 proxy authentication, because the client does not believe it is talking to a proxy.
  • REDIRECT gives you the original destination via SO_ORIGINAL_DST; TPROXY additionally lets the proxy keep the client's source address on the upstream connection.
  • Squid logs SECURITY ALERT: Host header forgery detected when an intercepted request's Host does not match the intercepted destination IP.

An intercepting proxy receives traffic the client never intended to send to a proxy, because the network diverted it. The client emits an ordinary origin-form request (GET /page HTTP/1.1 with a Host header) aimed at the origin's IP address, and a router, firewall or inline appliance steers those packets to a proxy instead. The proxy must then reconstruct the intended destination from the Host header and the original destination address, because nothing in the request tells it that a proxy is involved.

That reconstruction is the source of essentially every problem in this article.

Transparent and intercepting are different words#

The vocabulary is genuinely muddled, and the confusion costs people hours.

TermWhere it is definedWhat it means there
Transparent proxyRFC 2616 section 1.3A proxy that does not modify the request or response beyond what proxy authentication and identification require. The opposite is a non-transparent proxy, which rewrites content.
Interception proxyRFC 3040A proxy that receives traffic diverted by the network, without the client having been configured to use it.
"Transparent proxy" in vendor and operator usageEverywhere elseUsually means the RFC 3040 sense: the client is unaware.

So a proxy can be intercepting and non-transparent (it diverts your traffic and injects headers), or explicit and transparent (you configured it and it passes messages through untouched). Squid's own configuration reflects the split: the http_port option is spelled intercept, not transparent, precisely because the older name was ambiguous. Older Squid releases used transparent as a synonym; modern configuration should use intercept.

When you read "transparent proxy" in a runbook, resolve which sense is meant before acting on it.

How traffic gets diverted#

MechanismLayerOriginal destination preserved?Client IP to originRequiresMain drawback
Policy-based routing (PBR)3Yes, until the proxy's own rewriteProxy's IP unless spoofedRouter config, a next-hop to the proxyAsymmetric return paths, no per-flow selectivity beyond ACLs
WCCPv23/4, GRE or L2YesProxy's IP unless TPROXYCisco-style router plus a WCCP-speaking cacheVendor-specific, GRE MTU cost, redirect-list debugging is opaque
iptables REDIRECT4 (DNAT to local)Yes, via SO_ORIGINAL_DSTProxy's IPProxy on the gateway hostProxy must be on the forwarding path; cannot spoof client source
iptables/nftables TPROXY4Yes, on the accepting socketClient IP, if the proxy binds with IP_TRANSPARENTmangle table rules, fwmark policy routing, CAP_NET_ADMINReturn routing must come back through the proxy host
Inline bump-in-the-wire2YesDepends on the appliance's modeA bridge or tap physically in the pathFail-open/fail-closed decisions; a fault takes the link down
DNS interception7 (DNS)No, the destination IP is the proxy'sProxy's IPControl of the resolver or port 53Broken by DoH/DoT and by hardcoded resolvers; TLS name mismatch
Default-gateway or ARP manipulation2/3YesProxy's IPBeing the gatewayCoarse, all-or-nothing per subnet

DNS interception deserves a specific caution: it changes what a name resolves to, so the client opens TLS to an address that cannot present a valid certificate for that name unless you intercept TLS as well. It is also the mechanism most thoroughly defeated by modern clients, since DNS-over-HTTPS bypasses the local resolver entirely.

REDIRECT versus TPROXY on Linux#

REDIRECT is a DNAT target that rewrites the destination to a local address and port. The proxy accepts a normal socket, and recovers what the client originally asked for with a getsockopt(SO_ORIGINAL_DST) call:

bash
# Divert forwarded HTTP to a proxy listening on 3129
iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 3129
text
http_port 3129 intercept

The upstream connection is then made from the proxy's own address, so the origin sees the proxy.

TPROXY is different: it does not rewrite anything. It marks the packet and delivers it to a socket bound with the IP_TRANSPARENT option while the addressing stays intact, so the accepting socket already knows the original destination, and the proxy can also bind its outgoing socket to the client's address. The result is an origin that sees the real client IP.

bash
# Divert to a TPROXY-capable proxy, preserving both addresses
iptables -t mangle -N DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
iptables -t mangle -A DIVERT -j ACCEPT

# Established flows already owned by a local socket go straight to it
iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT

# New flows to port 80 are handed to the proxy on 3129
iptables -t mangle -A PREROUTING -p tcp --dport 80 \
    -j TPROXY --tproxy-mark 0x1/0x1 --on-port 3129

# Marked packets must be delivered locally, not routed onward
ip rule add fwmark 1 lookup 100
ip route add local default dev lo table 100
text
http_port 3129 tproxy

The -m socket -j DIVERT rule is the part people omit. Without it, packets belonging to an already-established intercepted connection are re-evaluated by the routing table, do not match a local socket by ordinary rules, and get forwarded away from the proxy; the symptom is that connections establish and then stall after the first exchange.

What interception breaks#

  • TLS on port 443. Redirecting 443 to a proxy that only relays gives you SNI-level visibility at best. Anything more requires generating a certificate per site from a private CA, which means TLS interception with a corporate root CA and installing that root in every client's trust store.
  • Certificate pinning. Applications that pin a public key or a specific CA reject the forged certificate and fail hard, usually with an opaque network error rather than a TLS message. Android has made this the default rather than the exception: apps targeting API level 24 and above trust only system CAs unless their network security configuration explicitly opts into user-installed CAs, so an enterprise root added by a user profile is ignored by most apps.
  • HSTS. RFC 6797 removes the click-through: a browser holding an HSTS entry, or seeing a preloaded domain, refuses to let the user accept an untrusted certificate at all. There is no user-facing workaround, only trust-store distribution.
  • Non-HTTP traffic on port 80. Interception rules select by port, not by protocol. Anything that speaks a custom protocol on 80 to traverse firewalls hits an HTTP parser and dies. The symptom is a connection that establishes and then closes immediately, or an HTTP error page delivered into a binary protocol stream.
  • IP-based authentication at the origin. A REDIRECT-based deployment replaces the client IP with the proxy's, so partner allow-lists keyed on office addresses break for everyone or, worse, start letting through traffic from any client behind the proxy. TPROXY avoids this; X-Forwarded-For does not help, because the origin is a third party that does not trust your headers. See the X-Forwarded-For header for why third-party origins ignore it.
  • QUIC and HTTP/3. Interception rules written for TCP do not see UDP 443 at all, so browsers that negotiate HTTP/3 silently bypass the proxy. The usual response is to block UDP/443 outbound and force fallback to TCP, which is worth planning for rather than discovering. See HTTP/2 and HTTP/3 through proxies.
  • Proxy authentication. This one is structural and not fixable: 407 Proxy Authentication Required and Proxy-Authorization only work when the client knows it is speaking to a proxy. An intercepted client believes it is talking to the origin, so a 407 is nonsense to it. Intercepting deployments must identify users by source IP, by an agent on the device, or by redirecting to a captive portal that sets a cookie. This is the strongest single argument for explicit configuration; see proxy authentication.

Squid: intercept mode versus explicit mode#

Squid treats the two as different ports with different parsing rules, and refuses to mix them.

Behaviourhttp_port 3128 (explicit)http_port 3129 intercept
Expected request-targetAbsolute-form (GET http://host/ ...) or CONNECTOrigin-form (GET / ...) with Host
Destination determinationFrom the request-targetFrom SO_ORIGINAL_DST, cross-checked against Host
Proxy auth (407)SupportedNot possible
CONNECTSupportedNot applicable, there is no CONNECT from an unaware client
Client awarenessYesNo
Typical error on a mismatched requestWorksInvalid Request / ERR_INVALID_REQ

The cross-check is the interesting part. Because a keep-alive connection is intercepted once but can carry requests for many hostnames, a client could open a connection to an innocuous IP and then send Host: internal.example.com on it, aiming the proxy at a destination the network policy never approved. Squid detects the mismatch and logs it in cache.log:

text
SECURITY ALERT: Host header forgery detected on local=93.184.216.34:80
  remote=10.0.3.17:51422 FD 14 flags=33 (local IP does not match any domain IP)
SECURITY ALERT: By user agent: curl/8.5.0
SECURITY ALERT: on URL: internal.example.com/

The immediate cause is often benign: DNS round-robin or a CDN handed the client one address while Squid's own resolution returned a different one for the same name, and the addresses legitimately differ. The fix is to make the proxy and the clients use the same resolver and respect the same TTLs, not to disable the check. The host_verify_strict directive controls how strictly this is applied to non-intercepted traffic; intercepted traffic is always verified, because without it interception is an open door into any destination reachable from the proxy. That overlaps directly with SSRF and the proxy layer.

A second gotcha: the proxy's own outbound traffic must be excluded from the redirect rules or it loops back in. On a gateway host, exempt by source and by owner:

bash
iptables -t nat -I PREROUTING 1 -p tcp --dport 80 -s 10.0.1.5 -j RETURN
iptables -t nat -I OUTPUT 1 -m owner --uid-owner proxy -j RETURN

How to detect you are behind one#

Run these from the client. Each isolates a different layer.

  1. Look for a proxy's fingerprint on plaintext HTTP. A Via header, an X-Cache header, or a Server: value that is not the origin's is conclusive.
    bash
    curl -sSI http://example.com/ | grep -iE 'via|x-cache|server|age'
  2. Use a captive-portal probe endpoint. http://connectivitycheck.gstatic.com/generate_204 returns 204 with an empty body when unmolested. Any other status, any body, or a redirect means something answered on the origin's behalf.
  3. Compare the certificate issuer against the public chain. If the issuer is an internal CA name, TLS is being intercepted.
    bash
    openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
      | openssl x509 -noout -issuer -subject -fingerprint -sha256
  4. Check whether the connection survives a bogus Host. Connect by IP with a Host for a domain that resolves elsewhere. A direct origin ignores the mismatch or serves its default site; an intercepting proxy frequently returns its own error page, and Squid logs the forgery alert shown above.
  5. Compare DNS answers. dig +short example.com @1.1.1.1 against the system resolver. Divergence points at DNS interception rather than packet redirection.
  6. Send a non-HTTP payload to port 80. printf 'PING\r\n' | nc example.com 80 should produce the origin's own behaviour; an HTTP 400 or an HTML error page means an HTTP parser is in the path.

Failure modes#

  • Connections establish then stall after a few kilobytes, TPROXY deployment. Missing -m socket -j DIVERT rule, or the fwmark routing rule is absent, so established-flow packets are routed onward instead of delivered locally.
  • ERR_INVALID_REQ from Squid on an intercept port. A proxy-aware client sent an absolute-form request to an intercept port. Give explicit clients a separate http_port and exclude their source addresses from the redirect rules.
  • Certificate warnings everywhere except on managed laptops. The private root reached the OS trust store but not containers or runtimes with their own bundles (Python certifi, Node's built-in roots, Java cacerts), each of which needs the CA added separately. See corporate proxies and developer tooling.
  • A specific mobile app fails while the browser works. Certificate pinning, or an Android app targeting API 24+ that ignores user-installed CAs. There is no proxy-side fix; exempt the destination from bumping.
  • Sporadic Host header forgery alerts for one busy CDN-hosted domain. DNS answers differ between client and proxy. Align resolvers and honour TTLs.
  • Proxy CPU climbs and traffic loops. Proxy egress is being re-intercepted. Exclude the proxy's own source address and UID from the redirect chain.
  • Long-lived streams die at a fixed interval. The proxy imposed its own idle timeout on a connection the client believed was direct. Server-sent events and WebSockets are the usual casualties; see proxy buffering and streaming responses.

Frequently asked questions#

What is the difference between a transparent proxy and an intercepting proxy?#

Strictly, "transparent" is RFC 2616 terminology for a proxy that does not modify messages, and "intercepting" is RFC 3040 terminology for a proxy that receives traffic diverted by the network without client configuration. In everyday operator usage "transparent proxy" almost always means the intercepting sense. Squid uses intercept as the configuration keyword to avoid the ambiguity.

Can a transparent proxy see HTTPS traffic?#

Not without terminating TLS. Redirecting port 443 to a proxy gives it the ClientHello, so it can see the SNI hostname, ALPN and TLS version, and it can allow or block on those. Reading URLs, headers or bodies requires generating certificates from a CA the client trusts, and that fails against pinned applications and does not work at all for HSTS-protected sites when the CA is missing.

Does an intercepting proxy preserve the client IP?#

Only with TPROXY or an equivalent transparent-source mechanism, where the proxy binds its upstream socket to the client's address using IP_TRANSPARENT. With REDIRECT, WCCP without TPROXY, or DNS-based diversion, the origin sees the proxy's address. Adding X-Forwarded-For does not help when the origin is a third party, since it has no reason to trust your headers.

Why does proxy authentication not work in intercept mode?#

Because 407 Proxy Authentication Required is a message from a proxy to a client that knows it is using one. An intercepted client thinks it is talking to the origin server, so a 407 is an unexpected status from the wrong party and the client will not send Proxy-Authorization. Intercepting deployments identify users by IP, by a device agent, or by a captive portal instead.

How do I tell whether my traffic is being intercepted right now?#

Check three things: whether plaintext HTTP responses carry Via or X-Cache headers, whether a known probe endpoint such as http://connectivitycheck.gstatic.com/generate_204 still returns an empty 204, and whether the TLS certificate for a well-known site is issued by a public CA or by an internal one. Any deviation on those three covers the common deployments.

Is interception better or worse than an explicit proxy?#

Explicit configuration is better wherever you can achieve it: it supports proxy authentication, gives clients a defined no_proxy escape hatch, produces clearer errors, and does not require you to own the routing path. Interception is the fallback for unmanaged devices and appliances that cannot be configured. A common compromise is explicit configuration via PAC or WPAD for managed clients, plus interception as a backstop for everything else, which is discussed in PAC files and WPAD.

Primary sources#

Every normative claim on this page is checked against the specification or the vendor documentation listed here. Where behaviour is version dependent, the version is named in the text.

  1. RFC 3040: Internet Web Replication and Caching Taxonomy
  2. RFC 2616: HTTP/1.1, section 1.3 Terminology
  3. RFC 6797: HTTP Strict Transport Security (HSTS)
  4. RFC 9112: HTTP/1.1, section 3.2 Request Target
  5. Squid configuration directive: http_port
  6. Squid feature: Linux TPROXY version 4.1+
  7. Linux kernel documentation: TPROXY
  8. Android developers: network security configuration and trusted CAs

Found something wrong, or behaviour that differs on your version? Report it with the version number and a primary source. Anything substantive is fixed in the page and logged on the corrections page. See editorial standards for how pages are researched, sourced and reviewed.

More in proxy fundamentals#