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 exceptionList still reaches the proxy.
Issue B (domain-scoped proxy not applied): When matchDomains is 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 from matchDomains, 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.com traffic is still received by the proxy.
Safari comparison:
If I access URLs that trigger the same *.cdninstagram.com hosts 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.com traffic is not received by the proxy.
Remove the host from matchDomains and observe that googlevideo.com traffic is received by the proxy.
Safari comparison:
If I access a googlevideo.com host from Safari while matchDomains = ["googlevideo.com"], it behaves as expected (proxied).
In contrast, the YouTube app’s googlevideo.com traffic 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.