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.
Are you actually building a VPN product?
I suspect that’s not the case, given that you’re using a placeholder for tunnelRemoteAddress. And if that’s true then… well… there are limits to how much I can help you here.
My experience is that developers often try to use a packet tunnel provider for things that are not VPN. DTS doesn’t support those efforts because they tend to be very brittle. One of my erstwhile colleagues even wrote a technote about this, namely, TN3120 Expected use cases for Network Extension packet tunnel providers.
If you’re trying to use a packet tunnel provider for something that isn’t VPN, I recommend that you watch WWDC 2025 Session 234 Filter and tunnel network traffic with NetworkExtension, where one of the engineers from the NE team explains the various alternatives that are available to you, including the new URL filter provider.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"