Skip to content

Support for HTTP/3#5296

Open
kylhuk wants to merge 17 commits intoNginxProxyManager:developfrom
kylhuk:develop
Open

Support for HTTP/3#5296
kylhuk wants to merge 17 commits intoNginxProxyManager:developfrom
kylhuk:develop

Conversation

@kylhuk
Copy link

@kylhuk kylhuk commented Feb 9, 2026

Added support for HTTP/3 since the OpenResty version supports it, so it was only a matter of adding it to the Nginx config templates and adding it to the frontend.

I am still testing, which is why this is a draft.

@kylhuk
Copy link
Author

kylhuk commented Feb 9, 2026

Affects the following issues:
#2834
#1550

@kylhuk kylhuk mentioned this pull request Feb 9, 2026
@toviszsolt
Copy link
Contributor

@kylhuk

  • I would like to draw your attention to the fact that PR is still in draft mode.
  • Can you please confirm that you have tested the build?
    nginxproxymanager/nginx-proxy-manager-dev:pr-5296

@kylhuk
Copy link
Author

kylhuk commented Feb 13, 2026

@toviszsolt Have not tested it yet, which is why it is still in draft mode. Will update you as soon as the PR is ready

@toviszsolt
Copy link
Contributor

@kylhuk

  • If you feel that your PR is ready and would like help with testing, I'm happy to help, just let me know.
  • Could you perhaps ask CoPilot for a review?

@Steve-Tech
Copy link

Hi, I tested this, I seem to be getting: {"nginx_online":false,"nginx_err":"nginx: [emerg] unknown directive \"http3\" in /data/nginx/proxy_host/1.conf:27\nnginx: configuration file /etc/nginx/nginx.conf test failed\n"}

I don't know if this error is printed anywhere reasonable, but it is saved in the database: select meta from proxy_host;.

I think you will have to add --with-http_v3_module here: https://github.com/NginxProxyManager/docker-nginx-full/blob/master/scripts/build-openresty

@jc21
Copy link
Member

jc21 commented Feb 17, 2026

I think you will have to add --with-http_v3_module here: https://github.com/NginxProxyManager/docker-nginx-full/blob/master/scripts/build-openresty

I've done this on the http3 branch:
NginxProxyManager/docker-nginx-full@f7a265b

Just waiting for a successful build before merging to master.

@toviszsolt
Copy link
Contributor

@jc21
Sounds good, I'm really looking forward to http3 being turned on. Let's do it, guys, go for it! If you need help, I'm here for you!

dependabot bot and others added 5 commits February 17, 2026 01:43
Bumps the prod-patch-updates group in /frontend with 2 updates: [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) and [country-flag-icons](https://gitlab.com/catamphetamine/country-flag-icons).


Updates `@tanstack/react-query` from 5.90.20 to 5.90.21
- [Release notes](https://github.com/TanStack/query/releases)
- [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md)
- [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.90.21/packages/react-query)

Updates `country-flag-icons` from 1.6.12 to 1.6.13
- [Changelog](https://gitlab.com/catamphetamine/country-flag-icons/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/catamphetamine/country-flag-icons/compare/v1.6.12...v1.6.13)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.90.21
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: country-flag-icons
  dependency-version: 1.6.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps the dev-minor-updates group in /frontend with 2 updates: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) and [happy-dom](https://github.com/capricorn86/happy-dom).


Updates `@biomejs/biome` from 2.3.14 to 2.4.0
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.4.0/packages/@biomejs/biome)

Updates `happy-dom` from 20.5.3 to 20.6.1
- [Release notes](https://github.com/capricorn86/happy-dom/releases)
- [Commits](capricorn86/happy-dom@v20.5.3...v20.6.1)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.4.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-minor-updates
- dependency-name: happy-dom
  dependency-version: 20.6.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-minor-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
@jc21
Copy link
Member

jc21 commented Feb 17, 2026

Just testing this image, a few things going on.

  1. When enabling http3 in the ui, the app reports it's successfully saved but something is going wrong, for me, and the conf file is removed from disk. Disabling the option and saving is actually successful though
  2. The UI for the buttons is weird now and needs some love
  3. The nginx template should also add the following to the server block when http3 is enabled:
# Advertise HTTP/3 support to clients
add_header Alt-Svc 'h3=":443"; ma=86400';

@jc21
Copy link
Member

jc21 commented Feb 17, 2026

Ok so there's a big problem as is.

[2/17/2026] [4:34:04 AM] [Nginx    ] › ⬤  debug     Nginx test failed: nginx: [emerg] duplicate listen options for 0.0.0.0:443 in /data/nginx/proxy_host/49.conf:23
nginx: configuration file /etc/nginx/nginx.conf test failed
[2/17/2026] [4:34:04 AM] [Nginx    ] › ⬤  debug     Deleting file: /data/nginx/proxy_host/3.conf

To replicate, enable http3 on any host. Then do it on another host.

listen 443 quic reuseport;

This doesn't like to be defined more than once across multiple server blocks. AI tells me:

reuseport only makes sense when multiple sockets are bound to the same 4‑tuple (IP:port + protocol family) so the kernel can distribute incoming packets across those sockets. With HTTP/3 (QUIC over UDP) in nginx, one UDP socket per address:port is effectively “owned” by a single ⁠server context (and nginx’s QUIC implementation expects to manage QUIC connection state off that one socket). If you try to declare ⁠listen 443 quic reuseport; in multiple ⁠server blocks for the same address:port, nginx treats it as an invalid/ambiguous configuration (multiple competing UDP QUIC listeners on the same endpoint), so it won’t allow it.

What to do instead
• Define the QUIC listener once (in a single ⁠server block) and route by SNI/Host as usual via ⁠server_name.
 • Keep your TCP/TLS HTTP/1.1+HTTP/2 ⁠listen 443 ssl; on multiple server blocks if you want—the QUIC ⁠listen ... quic is the one that must not be duplicated for the same IP:port.

If I'm being honest, this doesn't seem feasible given the current ecosystem of nginx config generation.

jc21 added 3 commits February 17, 2026 14:50
…ertificates

Fix uploading of custom certificates
…ndabot/npm_and_yarn/frontend/prod-patch-updates-95db6732c0

Bump the prod-patch-updates group in /frontend with 2 updates
…ndabot/npm_and_yarn/frontend/dev-minor-updates-d71d2fefd7

Bump the dev-minor-updates group in /frontend with 2 updates
@Steve-Tech
Copy link

Steve-Tech commented Feb 17, 2026

Could we put listen 443 quic reuseport; in docker/rootfs/etc/nginx/conf.d/default.conf and then use listen 443 quic; for the proxy hosts?

E.g: https://stackoverflow.com/a/77005737

@kylhuk
Copy link
Author

kylhuk commented Feb 17, 2026

Addressed the reuseport issue and added it to default.con and removed it from templates (38641bb)

@kylhuk kylhuk marked this pull request as ready for review February 17, 2026 14:17
@kylhuk
Copy link
Author

kylhuk commented Feb 17, 2026

@jc21 Feel free to modify this PR and or move it to a branch on your repo. Depending how big the reuseport issue is of course :)

Comment on lines +25 to +26
listen 443 ssl reuseport;
listen [::]:443 ssl reuseport;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should add a new line for each of the reuseport entries, I'm not sure what the implications and behaviour are with tacking it on the end of listen 443 ssl instead of listen 443 quic.

Unless you're sure this is fine.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am relying on the documentation of nginx and this says:

Syntax: 	listen address[:port] [default_server] [ssl] [http2 | quic] [proxy_protocol] [setfib=number] [fastopen=number] [backlog=number] [rcvbuf=size] [sndbuf=size] [accept_filter=filter] [deferred] [bind] [ipv6only=on|off] [reuseport] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];
            listen port [default_server] [ssl] [http2 | quic] [proxy_protocol] [setfib=number] [fastopen=number] [backlog=number] [rcvbuf=size] [sndbuf=size] [accept_filter=filter] [deferred] [bind] [ipv6only=on|off] [reuseport] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];
            listen unix:path [default_server] [ssl] [http2 | quic] [proxy_protocol] [backlog=number] [rcvbuf=size] [sndbuf=size] [accept_filter=filter] [deferred] [bind] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];
Default: 	

listen *:80 | *:8000;

Context: 	server

Source: https://nginx.org/en/docs/http/ngx_http_core_module.html#listen

According to that, listen 443 ssl reuseport; should be correct. But I have not tested it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi guys, I've written a more detailed comment here, I hope it will help: #5296 (comment)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it's syntactically correct, however SSL is TCP, while QUIC is UDP. So I don't think it'll be passed to the correct bind call.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it's syntactically correct, however SSL is TCP, while QUIC is UDP. So I don't think it'll be passed to the correct bind call.

Ah! Now I see what you are saying, and you are probably correct. The answer of @toviszsolt also supports your argument.

@toviszsolt
Copy link
Contributor

I have also done some research.

Snippets would be needed for http3:
snippets/http3-first.conf

# Use this ONLY in the first server block on :443/udp
listen 443 quic reuseport;
listen 443 ssl;
add_header Alt-Svc 'h3=":443"; ma=86400' always;

snippets/http3.conf

# Use this in all other server blocks on :443/udp
listen 443 quic;
listen 443 ssl;
add_header Alt-Svc 'h3=":443"; ma=86400' always;

Reuseport should only be specified once for the same IP:port combination. If you use it in the first server block with listen 443 quic reuseport;, all other server blocks on the same port should use listen 443 quic; without the reuseport directive. The operating system-level socket option will be shared across all worker processes and virtual hosts automatically, avoiding potential conflicts.

And it should be placed in the server block when http3 is enabled:

# First server block
server {
    include snippets/http3-first.conf;
    
    server_name example.tld;
    # ...
}

# All other server blocks
server {
    include snippets/http3.conf;
    
    server_name other.tld;
    # ...
}

It is important that Stream Hosts do not allow the option to enable http3

  • For HTTP based hosts (Proxy, Redirection, 404), http3 should work, as an HTTP response is sent.
  • For Stream, TCP/UDP forwarding happens instead of sending an HTTP response.

Additional fine-tuning parameters for the http3 protocol:
These parameters should be placed in the http block if you wish to set them.

http {
    # [RECOMMENDED]
    # Enable QUIC retry packets to validate client addresses 
    # and prevent IP spoofing attacks
    quic_retry on;
    
    # [SECURITY RISK]
    # Allow 0-RTT (zero round-trip time) early data for faster connection resumption
    # Warning: This may be vulnerable to replay attacks, use with caution
    ssl_early_data on;
    
    # [RECOMMENDED]
    # Enable Generic Segmentation Offloading for QUIC to improve performance
    # by offloading packet segmentation to the network card
    quic_gso on;
    
    # [RECOMMENDED]
    # Set buffer size for HTTP/3 request and response streams
    # Larger values may improve performance for big uploads/downloads
    http3_stream_buffer_size 64k;
}

@kylhuk
Copy link
Author

kylhuk commented Feb 17, 2026

Thanks @toviszsolt this was very helpful. Tried to implement a logic, so that we have a different config for the first http server. I did not test the new image yet, so I am not sure if my change affects also the Stream servers. The SSL buttons get rendered in frontend/src/components/Form/SSLOptionsFields.tsx

@nginxproxymanagerci
Copy link

Docker Image for build 5 is available on DockerHub:

nginxproxymanager/nginx-proxy-manager-dev:pr-5296

Note

Ensure you backup your NPM instance before testing this image! Especially if there are database changes.
This is a different docker image namespace than the official image.

Warning

Changes and additions to DNS Providers require verification by at least 2 members of the community!

*/
checkPrivateKey: async (privateKey) => {
const filepath = await tempWrite(privateKey, "/tmp");
const filepath = await tempWrite(privateKey);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems like a fair bug fix. The second parameter is optional, although lib does not specify where the temp file will be created. Reference: https://www.npmjs.com/package/temp-write

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic seems correct.

Copy link
Contributor

@toviszsolt toviszsolt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have reviewed all the changes and everything looks correct. I have left some comments on the modified files, which are just for information to help with the review process.

@7heMech

This comment has been minimized.

@7heMech

This comment has been minimized.

@toviszsolt
Copy link
Contributor

toviszsolt commented Feb 19, 2026

@7heMech
I'm sorry, but it seems you haven't read the history. The issue you mentioned has already been discussed and resolved. Can you please read all the previous comments on PR? Thanks.

Update: Please note that this PR does not affect Stream Hosts!

@7heMech

This comment has been minimized.

@toviszsolt
Copy link
Contributor

@7heMech
Thanks, but your PR doesn't affect this PR and vice versa. We have already discussed this in previous comments. Your PR only affects Stream Hosts.

Related part of backend/templates/_listen.conf

{% if tcp_forwarding == 1 or tcp_forwarding == true -%}
{% if udp_forwarding == 1 or udp_forwarding == true -%}

This PR omits Stream Hosts, precisely and it is intentionally NOT possible to enable http3 support for Stream Hosts in the UI. HTTP3 can only be enabled in the UI in the case of Proxy, Redirection, and Dead Hosts.

Related part of backend/templates/_listen.conf

{% if http3_support == 1 or http3_support == true %}

Modified files related to frontend modals:

  • frontend/src/modals/DeadHostModal.tsx
  • frontend/src/modals/ProxyHostModal.tsx
  • frontend/src/modals/RedirectionHostModal.tsx

NOT modified files related to frontend modals:

  • frontend/src/modals/StreamModal.tsx

@7heMech
Copy link
Contributor

7heMech commented Feb 19, 2026

@7heMech
Thanks, but your PR doesn't affect this PR and vice versa. We have already discussed this in previous comments. Your PR only affects Stream Hosts.

I didn't say they have something in common... I'm saying we should keep reuseport here like you originally intended just in another manner of activation.

@toviszsolt
Copy link
Contributor

@7heMech
I don't think the global settings you mentioned are the responsibility of this PR.
This PR deals with the implementation of enabling the HTTP/3 module and is not responsible for global settings outside the HTTP/3 protocol or Stream Host issues. Moreover, this PR does not even affect Stream Hosts. I understand that you are trying to solve a issue related to Stream Hosts, but this PR does not address a bug, but rather a feature.

@7heMech

This comment has been minimized.

@Steve-Tech
Copy link

Steve-Tech commented Feb 20, 2026

@7heMech, Like @toviszsolt said this doesn't effect Streams as that uses dedicated ports, whereas http3/quic is all port 443, and the quic reuseport issue has already been resolved (but I am yet to test). Also your comment with 00-init.conf isn't too helpful since we already know what we need to do, and it would conflict with the "You are not configured" page or whatever default page is enabled, adding logic to _listen.conf does seem like the best option and is what's already done.

Edit: Although I'm not sure what would happen if the http3_first host were deleted, does it just regenerate all the configs and pick a new http3_first?

@7heMech
Copy link
Contributor

7heMech commented Feb 20, 2026

adding logic to _listen.conf does seem like the best option and is what's already done.

Right, somehow I missed that...

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.

6 participants

Comments