Skip to content

nginx: fix HTTP/3 reuseport duplicates#5184

Open
Boubik wants to merge 1 commit intoopnsense:masterfrom
Boubik:fix/nginx-http3
Open

nginx: fix HTTP/3 reuseport duplicates#5184
Boubik wants to merge 1 commit intoopnsense:masterfrom
Boubik:fix/nginx-http3

Conversation

@Boubik
Copy link
Contributor

@Boubik Boubik commented Feb 3, 2026

Fix HTTP/3 reuseport duplicates

This PR fixes an Nginx config-generation issue introduced while adding optional HTTP/3 (QUIC) support in nginx: add optional HTTP/3 #5071.
When multiple server blocks ended up emitting reuseport for the same listen (same address:port), Nginx failed to start.

Reference PR: nginx: add optional HTTP/3 (#5071)


Problem

With HTTP/3 enabled on more than one server for the same address:port, the generated configuration could contain reuseport multiple times for the same socket.
reuseport is a socket option and must be applied consistently for a given address:port. When duplicated across multiple server blocks for the same listener, Nginx reports an error and refuses to start.


Fix

We now emit reuseport exactly once per address:port for HTTP/3:

  • The first generated HTTP/3 listen for a given address:port gets: quic reuseport
  • Any additional HTTP/3 listen directives for the same address:port get: quic (without reuseport)

This guarantees that the socket option is not duplicated and prevents Nginx from failing due to conflicting listen parameters.


Testing

Tested on a local OPNsense instance with multiple server blocks sharing the same address:port and HTTP/3 enabled on more than one of them.
If you have a different setup (especially more complex listener/server combinations), please give it a quick try — there may still be edge cases I did not cover.

@psychofaktory
Copy link

This fix works. Thanks

After this adjustment, however, the "HTTP/3 (QUIC)" option had to be set for all HTTP servers.

@fabianfrz
Copy link
Member

@psychofaktory so it breaks if not all servers have the flag set?

@psychofaktory
Copy link

At least that was my observation.

@fabianfrz
Copy link
Member

@psychofaktory

then it would make only sense as a global option and should not be configured per server or it is not configurable at all.

@Boubik
Copy link
Contributor Author

Boubik commented Mar 5, 2026

Thank you for testing this @psychofaktory.

Could you also try:

curl -I --http3-only https://yourdomain

I tested the scenario you described. Where HTTP3/QUIC was not enabled on all HTTP Servers and in my case it still behaved as expected.

My output:

curl -I --http3-only https://domain1.cz/
curl: (60) SSL: no alternative certificate subject name matches target hostname 'domain1.cz'
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the webpage mentioned above.

~ ❯ curl -I --http3-only https://domain2.cz/
HTTP/3
...

~ ❯ curl -I --http3-only https://domain3.cz
HTTP/3
...

HTTP3/QUIC was enabled on domain2 and domain3. After checking the Nginx config the reuseport was set on domain2. Domain1 was configured with HTTP/2 only.

@psychofaktory
Copy link

I have HTTP servers for one domain and for many subdomains of this domain.

If the HTTP/3 (QUIC) option is not enabled on any HTTP server, everything works perfectly.
If the HTTP/3 (QUIC) option (with this commit) is enabled on all HTTP servers, everything also works perfectly.

Here the result of curl -I --http3-only https://subdomain1.mydomain.com

HTTP/3 200
date: Thu, 05 Mar 2026 08:43:46 GMT
content-type: text/html; charset=utf-8
content-length: 8416
accept-ranges: bytes
last-modified: Wed, 04 Mar 2026 12:30:35 GMT
etag: "23a04085e06fb8de7fc27b664738b030"
server: mydomain.com
alt-svc: h3=":443"; ma=86400
referrer-policy: same-origin
x-content-type-options: nosniff
strict-transport-security: max-age=63072000; includeSubDomains; preload
permissions-policy: interest-cohort=()
expect-ct: enforce, max-age=604800
x-frame-options: SAMEORIGIN
x-xss-protection: 0

For testing purposes, I have now disabled HTTP/3 (QUIC) for subdomain1.mydomain.com only.

As a result, curl -I https://mydomain.com returns this error:

curl: (7) Failed to connect to mydomain.com port 443 after 1 ms: Could not connect to server

I really welcome the option to enable HTTP/3 (QUIC) via the GUI.
If implementation per server is not possible, then it should be possible to set the option globally.

@Boubik
Copy link
Contributor Author

Boubik commented Mar 5, 2026

Thank you, this is very useful feedback. It is interesting that your result differs from my testing. Maybe it is interfering with something else? If you are interested we can make more tests @psychofaktory.

I was also considering whether this should be implemented as a global option instead. However, in the current plugin, HTTP/2 is configured per HTTP server as well. Based on my understanding of nginx, HTTP/3/QUIC should in principle also be possible per site, so I initially followed that model.

That said, it seems I am probably not able to make the per-server approach work consistently for everyone. A global option may therefore be the better choice, but that is likely a decision for the maintainers of this repository / @fabianfrz.

If the maintainers decide that a global option is more appropriate, I can rework this PR in that direction with their guidance on where and how it should be exposed in the plugin.

@fabianfrz
Copy link
Member

If you ask me, I would vote for the global variant if per server is not possible. However this needs a proper documentation especially in the help texts and the changelog that if this is enabled, then all ports need to also whitelisted in the firewall so it is disabled by default and the admin can update the firewall rules before there are issues with the update as some people have auto updates on.

If you want to, you can also make a global variant per port (tokenize list) as this might work as well (for example support H3 on 443 but not on 8443).

@fichtner, @AdSchellevis: If this gets released, it probably needs a special mention in the release notes of the update.

@psychofaktory What do you think about this approch? I guess that is the most optimal solution.

@psychofaktory
Copy link

This proposal sounds perfect to me.

Basically, I would prefer a per-server approach, as settings could be selected more granularly. As far as I understand the specification, this should also be possible. However, one must also question the extent to which it makes sense to provide HTTP2 and then, in addition, HTTP3 only for other subdomains. At least in my setup, I see no reason not to provide HTTP3 for all HTTP servers. Therefore, a global option would be perfectly sufficient.
If this avoids the potential configuration problems mentioned, it would be better than not having the option to use HTTP3 at all.

Translated with DeepL.com (free version)

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants