Skip to content

Add HTTP/3 opt-in support with migrations, tests, and cleanup#5587

Open
RicoUHD wants to merge 21 commits into
NginxProxyManager:developfrom
RicoUHD:HTTP/3-support
Open

Add HTTP/3 opt-in support with migrations, tests, and cleanup#5587
RicoUHD wants to merge 21 commits into
NginxProxyManager:developfrom
RicoUHD:HTTP/3-support

Conversation

@RicoUHD
Copy link
Copy Markdown

@RicoUHD RicoUHD commented May 26, 2026

feat: add HTTP/3 (QUIC over UDP) opt-in support for proxy hosts 🚀

📖 Description

This PR implements full, opt-in support for HTTP/3 (QUIC over UDP) for proxy hosts. It covers the complete stack, including backend database schema migrations, template listener generations, dynamic Nginx capability detection, frontend toggle UI with multi-language localizations, and E2E integration tests.

The feature is designed from the ground up as a safe, zero-regression opt-in, ensuring that existing users upgrading their installations will experience exactly zero changes or configuration risk by default.


🔗 Fixes & Resolved Issues

This implementation natively satisfies and closes the long-standing community feature requests:

Fixes #1550

  • Closes HTTP3 + QUIC support #1550 — Introduces native HTTP/3 / QUIC support into the proxy engine generation layer.
  • Eliminates UI Overwrite Collisions — Resolves configuration loss for users attempting manual file overrides by treating QUIC as a first-class citizen inside the core orchestration engine.

🛡️ Key Architectural Highlights & Safety Guardrails

1. Robust Startup & Compatibility Protection (Non-QUIC Nginx Builds)

  • The Challenge: Nginx will refuse to boot if quic directives are present in configuration blocks on older binaries lacking the --with-http_v3_module compilation engine.
  • The Solution: Added a synchronous startup capability check in 55-http3-check.sh sourced inside the s6-overlay prepare oneshot service pipeline. If Nginx lacks HTTP/3 capabilities OR if NPM_HTTP3_DISABLED=1 is set, the startup script dynamically strips quic listen parameters from the static default.conf before Nginx launches, ensuring complete system stability.
  • Dynamic Health Reporting: The global capability state is exposed via the root API health router (/). If the core system detects that HTTP/3 is disabled or unsupported, the frontend hook intercepts the component mount and masks the UI elements entirely.
  • Idempotency Guard: Caches and restores the original default.conf on every container start, allowing operators to dynamically toggle the NPM_HTTP3_DISABLED kill-switch across restarts without container rebuilds.

2. Socket reuseport Conflict Resolution

  • The Strategy: Declaring reuseport multiple times on identical sockets crashes Nginx. The master QUIC socket is initialized globally as a singleton inside default.conf.
  • UI Sanitization: Added a strict line-anchored regex sanitizer in backend/internal/proxy-host.js (for both create and update operations) that programmatically strips manual reuseport declarations from user-submitted advanced_config textareas before database commit, preventing user-inflicted Nginx boot failures and cleaning up trailing punctuation cleanly.

3. Standards-Compliant API Type Coercion

  • Integrated http3_support natively into the model's standard boolFields lifecycle array. While the backend migration schema uses a storage-efficient tinyint format, the data layer automatically handles the type translation so that the API surfaces standard JSON booleans (true/false), matching the repo's established model design rules line-for-line.

4. Shadow-Safe Alt-Svc Header Inheritance

  • To satisfy Nginx's header shadowing rules (where nested location block headers completely replace/supersede outer server headers), the Alt-Svc header is declared dynamically inside the default / location block and custom location templates (_location.conf), ensuring reliable QUIC advertisement on the wire without duplicate header noise or parent scoping erasure.
  • Enables the correct TLS 1.3 profile required by QUIC while preserving TLS 1.2 compatibility for legacy TCP clients.

💾 Database Migration & Rollback

  • Up Migration: Alter Table migration (20260527000000_http3_support.js) adds http3_support as tinyint(1) defaulting to 0 (opt-in).
  • Defensive Rollback: The down migration defensively updates and zeroes all database rows before dropping the column to prevent template rendering errors during the hot reload cycle.

🎨 Frontend & Internationalization

  • Added a clean Enable HTTP/3 (QUIC) toggle switch in the SSLOptions modal tab layout.
  • Integrated translation keys (domains.http3-support) in all 22 supported languages to ensure zero internationalization debt across regional installations.

🧪 E2E Verification & Hardened Test Results

We carried out multiple layers of automated and manual sandbox testing to guarantee absolute deployment safety:

✅ 1. Active Nginx Configuration Compile Validation

We verified that the dynamically generated Nginx configurations parse successfully with zero conflicts under the new s6 pipeline:

Command: docker exec npm-app-1 nginx -t
Output:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

✅ 2. Live HTTP/3 Wire Advertisement Test

We queried the running upgraded container on port 443 over TLS forcing the exact SNI hostname (beta.lehn.site) and verified that the dynamic HTTP/3 advertisement header is active and correctly broadcasting:

Command: curl.exe -I -k --resolve beta.lehn.site:443:127.0.0.1 https://beta.lehn.site/
Output:

HTTP/1.1 404 Not Found
Server: openresty
Date: Tue, 26 May 2026 17:58:33 GMT
Connection: keep-alive
Strict-Transport-Security: max-age=63072000; preload

Alt-Svc: h3=":443"; ma=86400

✅ 3. Clean Baseline-to-Feature Upgrade & Data Integrity Verification

We deployed a clean production container running the current official release to initialize a fresh standard database, populated it with mock proxy host data, and then upgraded the container to this feature branch.

We ran an automated database validation pass to assert structural and data integrity:

Command: node verify_upgrade.js
Output:

Opening database natively to verify upgrade: .../database.sqlite
Table schema columns of proxy_host:
   - id (INTEGER)
   - created_on (datetime)
   - domain_names (json)
   ...
   - trust_forwarded_proto (tinyint)
   - http3_support (tinyint)

http3_support column exists? ✅ YES

Retrieved migrated Row 1:
   - id: 1
   - domain_names: ["test.lehn.site"]
   - http3_support: 0
   - http3_support value is 0? ✅ YES (Opt-in default intact)

=============================================
🎉 UPGRADE AND MIGRATION VERIFIED SUCCESSFULLY!
=============================================

RicoUHD and others added 21 commits May 26, 2026 16:41
This change implements the full HTTP/3 opt-in capability for nginx-proxy-manager. Includes database migrations, backend schemas, template support for listen directives, UI toggle switches, Cypress E2E API tests, s6-overlay startup checks, and Docker Compose configurations.
Reverts docker-compose.yml to the standard production configuration and deletes sandbox-specific testing scripts.
Implements synchronous startup Nginx HTTP/3 binary capability checks, replaces window config lookup with a dynamic backend health check query in React, removes the hardware-dependent quic_gso directive, and tightens the advanced config reuseport sanitizer regex.
Hooks s6 55-http3-check.sh script into the oneshot prepare pipeline, resolves default.conf startup failures on non-QUIC or disabled hosts by dynamically stripping quic listeners at boot, and resolves api-level types by mapping http3_support to standard knex boolFields.
…-Svc header

Implements copy-on-write idempotency for default.conf during s6 startup to allow runtime HTTP/3 enable/disable toggles across restarts, removes redundant server-level Alt-Svc headers from templates to avoid shadowing confusion, and cleans up stray spaces in the advanced config sanitizer.
Sets docker-compose to build dynamically from the local Dockerfile on standard ports 80/443 for seamless developer onboarding.
Reverts the root docker-compose.yml to reference the standard jc21/nginx-proxy-manager:latest production image, keeping the PR perfectly clean and free of development-only compilation scopes.
Add HTTP/3 opt-in support for proxy hosts with migrations and tests
Restore missing isolated CI compose overlay for Jenkins orchestration
Updated CI docker-compose file to include build context and additional environment variables for services.
Never worked with Jenkins before :)
…evious CI breakage)

Apologies for the previous commit which broke Jenkins by duplicating databases, using invalid Cypress arguments, and setting mismatched build contexts. This cleanly inherits from docker-compose.ci.yml to run HTTP/3 E2E specs on the standard testing network.
…Cypress E2E API validation

Apologies for overlooking the health check schema validation! The health route GET /api returns the 'http3_disabled' property, but the swagger schema didn't define it and rejected additional properties, causing Cypress E2E tests to fail in the 'before all' hook. This resolves the validation failure.
@nginxproxymanagerci
Copy link
Copy Markdown

Docker Image for build 10 is available on DockerHub:

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

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!

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 + QUIC support

2 participants