Skip to content

Set the QUIC InitialPacketSize to 1200 bytes#7886

Open
Salynn wants to merge 1 commit into
caddyserver:masterfrom
Salynn:tailscale-h3
Open

Set the QUIC InitialPacketSize to 1200 bytes#7886
Salynn wants to merge 1 commit into
caddyserver:masterfrom
Salynn:tailscale-h3

Conversation

@Salynn

@Salynn Salynn commented Jul 15, 2026

Copy link
Copy Markdown

quic-go defaults the InitialPacketSize to 1280 bytes. This is used for the size of the payload plus UDP header. When quic-go creates its initial handshake packet, it pads it to this size as an optimization for the anti-amplification limit (which does not really apply to Caddy's server connections).

Tailscale uses an MTU of 1280 (the minimum IPv6 MTU) because it is a tunnel operating in unknown environments, possibly inside other tunnels.

When wrapped in an IP header (either v4 or v6), the 1280 byte QUIC packet constructed by quic-go exceeds the Tailscale MTU and gets dropped. This makes it impossible to respond to an HTTP3 connection attempt over Tailscale when using the default InitialPacketSize.

We explicitly set the InitialPacketSize to 1200 (its minimum) to support HTTP3 connections over Tailscale and other low MTU connections.

Once the connection is established, quic-go can perform MTU discovery to increase the packet size to the maximum supported by the connection, so any throughput loss due to the lowered InitialPacketSize is short-lived.

No AI was used in making this PR.

Fixes: #7885

@CLAassistant

CLAassistant commented Jul 15, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@Salynn

Salynn commented Jul 15, 2026

Copy link
Copy Markdown
Author

I did not see any mechanism to test this with the current Caddy test code, but I am open to suggestions for how to write tests for this.
I have applied this patch to my local build of Caddy on both Windows and Linux and it has fixed HTTP3 connections over Tailscale on both.

quic-go defaults the `InitialPacketSize` to 1280 bytes. This is used for
the size of the payload plus UDP header. When quic-go creates its
initial handshake packet, it pads it to this size as an optimization
for the anti-amplification limit (which does not really apply to
Caddy's server connections).

Tailscale uses an MTU of 1280 (the minimum IPv6 MTU) because it is a
tunnel operating in unknown environments, possibly inside other
tunnels.

When wrapped in an IP header (either v4 or v6), the 1280 byte QUIC packet
constructed by quic-go exceeds the Tailscale MTU and gets dropped. This
makes it impossible to respond to an HTTP3 connection attempt over
Tailscale when using the default `InitialPacketSize`.

We explicitly set the `InitialPacketSize` to 1200 (its minimum) to support
HTTP3 connections over Tailscale and other low MTU connections.

Once the connection is established, quic-go can perform MTU discovery to
increase the packet size to the maximum supported by the connection, so
any throughput loss due to the lowered `InitialPacketSize` is
short-lived.
@francislavoie

Copy link
Copy Markdown
Member

FYI @marten-seemann in case you have any opinions on this

@marten-seemann

Copy link
Copy Markdown
Contributor

This is a known issue: tailscale/tailscale#2633

Tailscale has been refusing to fix this for years. I think Tailscale should do their homework, and not expect the rest of the internet to work around their bugs...

For reference, Firefox also uses the exact same initial packet size as quic-go (1280 bytes), and therefore won't be able to do QUIC over Tailscale either.

@Salynn

Salynn commented Jul 21, 2026

Copy link
Copy Markdown
Author

For reference, Firefox also uses the exact same initial packet size as quic-go (1280 bytes), and therefore won't be able to do QUIC over Tailscale either.

@marten-seemann cite your source, please? I exclusively use Firefox, and it does not have this issue. It's initial handshake connection packet fits well within Tailscale's MTU, and once I applied this patch locally, I'm able to use HTTP3 over Tailscale to connect to Caddy from my Firefox.

@marten-seemann

Copy link
Copy Markdown
Contributor

They might look at the interface MTU, but normally they use 1280 as the initial packet size.

@Salynn

Salynn commented Jul 21, 2026

Copy link
Copy Markdown
Author

Again, I ask you to cite your source? Because whatever they're doing, it works to send packets that fit within a 1280 MTU. quic-go does not, and every request to make it do so has been rejected.

In fact, they have been rejected because you have provided the ability to lower the InitialPacketSize via API. So this PR is attempting to do exactly what you have told people asking for quic-go to work with smaller MTUs to do.

I don't understand the attempt to discourage this change in favor of a Tailscale change that is definitely not coming soon and may never come.

@Salynn

Salynn commented Jul 21, 2026

Copy link
Copy Markdown
Author

I have just done a pcap and the Initial packet from Firefox has a packet size of 1280, and a UDP size of 1252. So perhaps the use of IntialPacketSize as the size of the UDP portion of the packet and not the entire packet in quic-go is the difference?

@mxinden

mxinden commented Jul 22, 2026

Copy link
Copy Markdown

👋 Firefox engineer here, working on Firefox's QUIC stack.

We never go below 1280 based on the interface MTU.

neqo's PMTUD search table starts at 1280 for both v4 and v6, and the default PLPMTU is that first entry minus the header: 1252 (v4, header 28) / 1232 (v6, header 48), inside a 1280-byte packet. See default_plpmtu / MTU_SIZES_*.

The iface_mtu is only an upper bound: next() stops probing upward once the next table entry exceeds mtu_limit. Probing always starts at probe_index = 0 (the 1280 base), and there is no path below it. A small interface MTU (e.g. Tailscale's 1280) just means we never probe above 1280, never beneath.

Firefox also ships PMTUD off by default (network.http.http3.pmtud = false, wired in neqo_glue/src/lib.rs), so it stays pinned at the 1252/1232 default regardless.

You can experiment with this yourself, enabling PMTUD via about:config, search for pmtud.

Let me know if this is helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP3 does not work over Tailscale

5 participants