I’m working on an iOS Network Extension where a NEPacketTunnelProviderconfigures a local HTTP/HTTPS proxy usingNEPacketTunnelNetworkSettings.proxySettings.
Per NEProxySettings.exceptionList docs:
If the destination host name of an HTTP connection matches one of these patterns then the proxy settings will not be used for the connection.
However, I’m seeing two distinct issues:
- Issue A (exception bypass not working): HTTPS traffic to a host that matches
exceptionListstill reaches the proxy. - Issue B (domain-scoped proxy not applied): When
matchDomainsis set to match a specific domain (example:["googlevideo.com"]), I still observe its traffic in some apps is not proxied. If I remove the domain frommatchDomains, the same traffic is proxied.
Environment
- OS: iOS (reproduced with 26.4 and other versions)
- Devices: Reproduced with several iPhones (likely iPads as well)
- Xcode: 26.3
- Extension:
NEPacketTunnelProvider
Minimal Repro (code)
This is the minimal configuration. Toggle between CONFIG A / CONFIG B to reproduce each issue.
import NetworkExtension
final class PacketTunnelProvider: NEPacketTunnelProvider {
override func startTunnel(
options: [String : NSObject]? = nil,
completionHandler: @escaping (Error?) -> Void
) {
let proxyPort = 12345 // proxy listening port
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "8.8.8.8")
let proxySettings = NEProxySettings()
proxySettings.httpEnabled = true
proxySettings.httpsEnabled = true
proxySettings.httpServer = NEProxyServer(address: "1.2.3.4", port: proxyPort) // proxy listening address
proxySettings.httpsServer = NEProxyServer(address: "1.2.3.4", port: proxyPort) // proxy listening address
// CONFIG A: proxy all domains, but exclude some domains
// proxySettings.matchDomains can be set to match all domains
// proxySettings.exceptionList = ["*.cdninstagram.com", "cdninstagram.com"]
// CONFIG B: proxy only a specific domain
// proxySettings.matchDomains = ["googlevideo.com"]
settings.proxySettings = proxySettings
setTunnelNetworkSettings(settings) { error in
completionHandler(error)
}
}
}
Repro steps
Issue A (exceptionList bypass not working)
- Enable the VPN configuration and start the tunnel with CONFIG A (
exceptionList = ["*.cdninstagram.com", "cdninstagram.com"]). - Open the Instagram app to trigger HTTPS connections to
*.cdninstagram.com - Inspect proxy logs:
cdninstagram.comtraffic is still received by the proxy.
Safari comparison:
- If I access URLs that trigger the same
*.cdninstagram.comhosts from Safari, it can behave as expected. - When the traffic is triggered from the Instagram app, the excluded host still reaches the proxy as
CONNECT, which is unexpected.
Issue B (matchDomains not applied for YouTube traffic)
- Start the tunnel with CONFIG B (
matchDomains = ["googlevideo.com"]). - Open the YouTube app and start playing a video (traffic typically targets
*.googlevideo.com). - Inspect proxy logs:
googlevideo.comtraffic is not received by the proxy. - Remove the host from
matchDomainsand observe thatgooglevideo.comtraffic is received by the proxy.
Safari comparison:
- If I access a
googlevideo.comhost from Safari whilematchDomains = ["googlevideo.com"], it behaves as expected (proxied). - In contrast, the YouTube app’s
googlevideo.comtraffic is not proxied unless I match all domains.
Expected
Issue A
Connections to *.cdninstagram.com in the Instagram app should not use the proxy and should not reach the local proxy server.
Issue B
With matchDomains = ["googlevideo.com"], traffic to *.googlevideo.com (YouTube video traffic) should be proxied and therefore reach the local proxy.
Actual
Issue A
The local proxy still receives the request as:
CONNECT scontent-mad1-1.cdninstagram.com:443 HTTP/1.1
So the bypass does not happen.
Issue B
With matchDomains = ["googlevideo.com"], I still observe googlevideo.com traffic in the YouTube app that is not delivered to the proxy. When all traffic is proxied, the same traffic is delivered to the proxy.