Performance degradation of HTTP/3 requests in iOS app under specific network conditions

Hello Apple Support Team,

We are experiencing a performance issue with HTTP/3 in our iOS application during testing.

Problem Description: Network requests using HTTP/3 are significantly slower than expected. This issue occurs on both Wi-Fi and 4G networks, with both IPv4 and IPv6. The same setup worked correctly in an earlier experiment.

Key Observations:

  1. The slowdown disappears when the device uses: · A personal hotspot. · Network Link Conditioner (with no limitations applied). · Internet sharing from a MacBook via USB (where traffic was also inspected with Wireshark without issues).
  2. The problem is specific to HTTP/3 and does not occur with HTTP/2.
  3. The issue is reproducible on iOS 15, 18.7, and the latest iOS 26 beta.
  4. HTTP/3 is confirmed to be active (via assumeHttp3Capable and Alt-Svc header).
  5. Crucially, the same backend endpoint works with normal performance on Android devices and using curl with HTTP/3 support from the same network.
  6. I've checked the CFNetwork logs in the Console but haven't found any suspicious errors or obvious clues that explain the slowdown.
  7. We are using a standard URLSession with basic configuration.
  8. Attempted to collect qlog diagnostics by setting the QUIC_LOG_DIRECTORY=~/ tmp environment variable, but the logs were not generated.

Question: What could cause HTTP/3 performance to improve only when the device is connected through a hotspot, unrestricted Network Link Conditioner, or USB-tethered connection? The fact that Android and curl work correctly points to an issue specific to the iOS network stack. Are there known conditions or policies (e.g., related to network interface handling, QoS, or specific packet processing) that could lead to this behavior?

Additionally, why might the qlog environment variable fail to produce logs, and are there other ways to obtain detailed HTTP/3 diagnostic information from iOS?

Any guidance on further diagnostic steps or specific system logs to examine would be greatly appreciated.

Thank you for your assistance.

Answered by DTS Engineer in 863163022
we can't reproduce this issue in the iOS simulator.

OK.

I asked that for a bunch of reasons:

  • The simulator offers more network debugging options.
  • If something reproduces in the simulator, I’d expect it to reproduce on macOS itself, which opens up further options.
  • The next step is to file a bug, and the best way to do that depends on the platform.

IMO you should file a bug about this. It’d be ideal if you could include:

  • A small test project that reproduces the problem.
  • A sysdiagnose log taken on the device immediately after reproducing the problem.
  • After having installed the Network Diagnostics debug profile from our Bug Reporting > Profiles and Logs page.
  • An RVI packet trace of the fast and slow cases. Presuming that just enabling RVI doesn’t resolve the problem, of course (-:

Please post your bug number, just for the record.


Attempted to collect qlog diagnostics by setting the QUIC_LOG_DIRECTORY=~/tmp

That won’t work because ~ is expanded by the shell, and there is no shell on iOS. Moreover, you can’t use a fixed path because the root of the iOS app’s container changes all the time.

You can enable QUIC diagnostics by adding code like this to your app’s startup:

let tmpDir = FileManager.default.temporaryDirectory
let qlogDir = tmpDir.appendingPathComponent("qlog-\(Date.now)", isDirectory: true)
try! FileManager.default.createDirectory(at: qlogDir, withIntermediateDirectories: true)
setenv("QUIC_LOG_DIRECTORY", qlogDir.path, 1)

Make sure this runs before you do anything with networking.

If you do this then you’ll likely run into an annoying gotcha that I ran in to, namely that:

  • The QUIC diagnostic files have a colon in the name.
  • Which causes the Download Container command in the Devices and Simulators window to fail (r. 163262484)-:

If you’re committed to digging into the QUIC log, you’ll need to find some other way to get these files off the device. You could, for example, add a command to your app that zips them up, and removes the originals, allowing Download Container to do its thing.


The slowdown disappears when the device uses …

I’m guessing that all of these nobble the user space networking stack, and that’s the deciding factor here. However, that’s really just a guess. And, annoyingly, it doesn’t yield any actionable workaround.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Does this reproduce in the simulator?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you for your response.

No, we can't reproduce this issue in the iOS simulator.

we can't reproduce this issue in the iOS simulator.

OK.

I asked that for a bunch of reasons:

  • The simulator offers more network debugging options.
  • If something reproduces in the simulator, I’d expect it to reproduce on macOS itself, which opens up further options.
  • The next step is to file a bug, and the best way to do that depends on the platform.

IMO you should file a bug about this. It’d be ideal if you could include:

  • A small test project that reproduces the problem.
  • A sysdiagnose log taken on the device immediately after reproducing the problem.
  • After having installed the Network Diagnostics debug profile from our Bug Reporting > Profiles and Logs page.
  • An RVI packet trace of the fast and slow cases. Presuming that just enabling RVI doesn’t resolve the problem, of course (-:

Please post your bug number, just for the record.


Attempted to collect qlog diagnostics by setting the QUIC_LOG_DIRECTORY=~/tmp

That won’t work because ~ is expanded by the shell, and there is no shell on iOS. Moreover, you can’t use a fixed path because the root of the iOS app’s container changes all the time.

You can enable QUIC diagnostics by adding code like this to your app’s startup:

let tmpDir = FileManager.default.temporaryDirectory
let qlogDir = tmpDir.appendingPathComponent("qlog-\(Date.now)", isDirectory: true)
try! FileManager.default.createDirectory(at: qlogDir, withIntermediateDirectories: true)
setenv("QUIC_LOG_DIRECTORY", qlogDir.path, 1)

Make sure this runs before you do anything with networking.

If you do this then you’ll likely run into an annoying gotcha that I ran in to, namely that:

  • The QUIC diagnostic files have a colon in the name.
  • Which causes the Download Container command in the Devices and Simulators window to fail (r. 163262484)-:

If you’re committed to digging into the QUIC log, you’ll need to find some other way to get these files off the device. You could, for example, add a command to your app that zips them up, and removes the originals, allowing Download Container to do its thing.


The slowdown disappears when the device uses …

I’m guessing that all of these nobble the user space networking stack, and that’s the deciding factor here. However, that’s really just a guess. And, annoyingly, it doesn’t yield any actionable workaround.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Oh, one extra thing. In iOS 26 we added the usesClassicLoadingMode property, which controls whether the session uses the ‘classic’ or ‘modern’ HTTP engine. If you disable this, and thus switch to the modern engine, does that change things?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Performance degradation of HTTP/3 requests in iOS app under specific network conditions
 
 
Q