HTTP/3 has been standardized for years and is already available in several major web servers, proxies and content delivery networks. Yet Apache HTTP Server—one of the most widely deployed web servers in the world—still does not provide production-ready HTTP/3 support in its current stable release.
Apache httpd 2.4.68 officially supports HTTP/1.x and HTTP/2, but its bundled module list contains mod_http2 and no corresponding mod_http3. An external experimental mod_http3 project now exists, but it targets Apache’s development trunk and is explicitly described as experimental.
That is why Apache HTTP Server pull request #699 is particularly interesting.
The pull request does not implement the entire HTTP/3 protocol. Instead, it modifies Apache’s Multi-Processing Modules, or MPMs, so that they can correctly account for connections created outside their traditional TCP accept loop.
This may sound like a small internal change, but it addresses one of the architectural barriers preventing a QUIC-based HTTP/3 module from integrating cleanly with Apache’s process lifecycle.
For Apache users waiting for first-class HTTP/3 support, PR #699 is a promising sign that the necessary core infrastructure is beginning to appear.
At CodeIT.guru, we also attempted to backport this MPM work from Apache development trunk to the current Apache 2.4.68 stable branch. Our objective was to evaluate how much of the required HTTP/3 infrastructure could be brought to existing Apache installations without waiting for a future major release.
The experiment reinforced an important conclusion: PR #699 is valuable, but it is one building block in a larger HTTP/3 effort rather than a standalone patch that instantly enables HTTP/3 on Apache 2.4.
The long-awaited HTTP/3 gap in Apache
HTTP/3 is no longer an experimental browser protocol. It is the standardized mapping of HTTP over QUIC, with QUIC transported over UDP instead of TCP.
Among its most important practical advantages are:
- faster connection establishment;
- integrated TLS 1.3 security;
- reduced transport-level head-of-line blocking;
- connection migration when a client changes networks;
- better behavior on high-latency or lossy networks.
For example, a mobile device may move from Wi-Fi to a cellular connection while keeping the same logical QUIC session. A traditional TCP connection is identified by network addresses and ports and will normally need to be re-established when those values change. QUIC uses connection identifiers that can survive such network transitions.
HTTP/3 is therefore particularly useful for mobile applications, international services and users on unreliable networks.
Apache’s lack of native, stable HTTP/3 support has become increasingly noticeable. Administrators who want to retain Apache as an application server often have to place an HTTP/3-capable proxy or CDN in front of it. The edge server terminates HTTP/3 and forwards the request to Apache using HTTP/2 or HTTP/1.1.
That architecture works, but it means Apache itself is not handling the HTTP/3 connection.
The official Apache 2.4 documentation lists HTTP/2 support through mod_http2, while the current stable module index contains no bundled HTTP/3 transport module. Apache 2.4.68 remains the project’s recommended stable release as of August 5, 2026.
An experimental external mod_http3 project is now working to close this gap. Its repository provides a module that adds HTTP/3 over QUIC and demonstrates a configuration using:
Protocols h3 h2 http/1.1
However, the project clearly labels its current status as experimental. Its default build also compiles development versions of Apache and related dependencies rather than simply targeting an ordinary packaged Apache 2.4 installation.
This is the broader context in which PR #699 matters.
Why HTTP/3 does not fit Apache’s traditional architecture
Apache HTTP Server has historically assumed that its MPM controls the network connection lifecycle.
The MPM:
- owns or manages the listening sockets;
- accepts incoming TCP connections;
- assigns those connections to processes or threads;
- tracks active work;
- decides when a child process can safely exit.
This model works naturally for HTTP/1.1 and HTTP/2 because both normally operate over TCP connections accepted by Apache’s standard listener infrastructure.
HTTP/3 is different.
HTTP/3 runs over QUIC, and QUIC uses UDP datagrams. The QUIC implementation—not the operating system’s TCP stack—handles concepts such as:
- reliable delivery;
- retransmission;
- stream multiplexing;
- flow control;
- congestion control;
- encryption;
- connection establishment;
- connection migration.
An HTTP/3 module may therefore create and manage its own UDP listener instead of receiving ordinary TCP connections from the active Apache MPM.
The module understands that a QUIC connection is active.
The MPM may not.
That creates a serious lifecycle problem.
The graceful-restart problem
Graceful reloads are one of Apache’s most important operational features.
An administrator can run:
apachectl graceful
or use the operating system’s service manager to reload Apache.
The new Apache generation begins accepting traffic while old child processes are allowed to finish their existing work. This permits configuration changes, certificate updates and deployments without immediately terminating active client requests.
For ordinary TCP connections, the MPM knows whether the child is still busy.
But imagine an Apache child serving several HTTP/3 clients through a module-owned UDP listener.
The HTTP/3 module knows those QUIC connections remain active. Because the MPM did not accept them through its normal TCP path, it may see no remaining connections of its own.
From the MPM’s perspective, the child appears ready to terminate.
From the HTTP/3 module’s perspective, the child still owns live connections and active streams.
Without coordination, a graceful Apache reload could interrupt HTTP/3 sessions even though ordinary HTTP/1.1 and HTTP/2 sessions are allowed to drain properly.
Possible consequences include:
- broken client connections during Apache reloads;
- interrupted downloads or API responses;
- inconsistent graceful-shutdown behavior;
- crashes caused by assumptions about MPM-owned connection data;
- MPM-specific workarounds inside the HTTP/3 module;
- child processes terminating before QUIC cleanup completes.
PR #699 creates a formal way for modules and MPMs to coordinate.
What PR #699 adds
The pull request proposes two optional MPM functions:
ap_mpm_note_extra_connection_added()
ap_mpm_note_extra_connection_removed()
A module calls the first function when it creates or accepts a connection outside the MPM’s standard connection path.
It calls the second when that connection is completely closed.
The lifecycle becomes:
HTTP/3 module creates a QUIC connection
↓
Module reports an extra connection to the MPM
↓
The MPM includes it in the child lifecycle
↓
A graceful restart begins
↓
The old child remains alive
↓
QUIC streams and connection cleanup complete
↓
Module removes the extra connection
↓
The MPM may safely terminate the child
The pull request currently targets the Apache development trunk and remains open. It includes changes for the event, worker and prefork MPMs.
The optional-function design is also important. It establishes a narrow interface between modules and MPMs without requiring the HTTP/3 module to depend directly on private implementation details from a particular MPM.
The module only needs to communicate two events:
- an externally managed connection now exists;
- that connection no longer exists.
Each MPM remains free to implement the accounting according to its own internal concurrency model.
Changes to the event MPM
The event MPM is generally the most relevant MPM for modern high-concurrency Apache deployments.
Unlike a traditional thread-per-connection model, the event MPM can move certain waiting connections away from worker threads, allowing those threads to process other requests.
For externally managed connections, PR #699 integrates the additional lifecycle count with the event MPM’s existing connection accounting.
The patch also handles an important shutdown condition: when the final extra connection disappears, the event loop may need to be awakened so it can observe the updated state and finish shutting down.
Changing an atomic counter alone is not sufficient if the relevant thread remains asleep inside a poll operation. Waking the pollset lets the MPM re-evaluate whether the child can now exit.
Changes to worker and prefork
The worker MPM uses multiple processes with multiple threads in each process. The patch gives it equivalent accounting for module-owned connections and makes graceful child termination wait for those connections to disappear.
The prefork MPM uses separate single-threaded child processes rather than threaded workers. Even there, an external module can own a transport or connection whose lifecycle must prevent premature child termination.
Supporting all three major Unix MPMs gives a protocol module a consistent contract:
The module owns the transport connection, but the MPM remains responsible for the process lifecycle.
The pull request does not currently provide equivalent changes for every platform-specific MPM, such as the Windows mpm_winnt implementation. Its initial practical scope is therefore focused primarily on Unix-like Apache deployments.
Preventing unsafe assumptions about connection ownership
PR #699 also addresses another important issue in the event MPM.
Apache modules can create logical or subordinate connection records. Multiplexed protocols may represent individual streams or related processing contexts through additional conn_rec structures.
Historically, event-MPM code could assume that a master connection was originally created and configured by the MPM itself.
That assumption is not always valid for an HTTP/3 module.
A QUIC connection created by the module may not contain the private event-MPM configuration that an ordinary MPM-managed TCP connection would contain. Attempting to use that missing data can cause invalid memory access or a crash.
The proposed changes add protection for connections whose master is not managed by the MPM.
This represents a broader architectural recognition: in a server that supports user-space transports such as QUIC, not every meaningful connection object necessarily begins inside the MPM.
Why PR #699 is promising for Apache HTTP/3
PR #699 does not suddenly make the following command work on Apache 2.4.68:
Protocols h3 h2 http/1.1
It does, however, address a necessary part of making that configuration reliable in the future.
Graceful HTTP/3 connection draining
A production HTTP/3 implementation must survive ordinary server administration.
That includes:
- configuration reloads;
- certificate renewals;
- child-process recycling;
- software deployments;
- service reloads;
- controlled shutdowns.
HTTP/3 support that works only until the first graceful reload is not production-ready.
By letting the MPM know about active QUIC connections, PR #699 creates the basis for graceful HTTP/3 draining.
Cleaner module architecture
Without a common MPM API, an HTTP/3 module might need to:
- modify each MPM separately;
- access private MPM data;
- duplicate shutdown logic;
- delay shutdown through unsupported workarounds;
- maintain different code paths for event, worker and prefork.
That would make the module fragile and difficult to upstream or maintain.
A small public lifecycle interface is a much cleaner design.
Support for transport protocols beyond TCP
Although HTTP/3 is the immediate reason for the work, the proposed functions are not named specifically for QUIC.
They describe “extra connections.”
That makes the concept reusable by future modules that:
- own UDP or datagram listeners;
- implement user-space transports;
- maintain long-lived logical sessions;
- create connections outside the MPM accept loop;
- need to participate in graceful process shutdown.
PR #699 is therefore not merely an HTTP/3 workaround. It is a modest generalization of Apache’s connection ownership model.
CodeIT.guru’s Apache 2.4.68 backport experiment
Because Apache 2.4.68 is the current stable version deployed by administrators today, CodeIT.guru attempted to backport PR #699 from Apache trunk to the 2.4.68 codebase.
The goal was not simply to make the patch compile. We wanted to understand whether this part of the developing HTTP/3 architecture could be used with the stable Apache branch.
This distinction matters:
Patch applies successfully
≠
Complete HTTP/3 support works on Apache 2.4.68
PR #699 modifies MPM lifecycle behavior, but the experimental mod_http3 project also relies on other Apache trunk APIs and development work. Backporting the MPM changes alone cannot provide the entire environment expected by the HTTP/3 module.
Our attempt therefore demonstrated both the value and the limits of this PR.
The value is clear: the connection-accounting design can potentially be adapted to the 2.4 architecture, giving externally managed QUIC connections a way to participate in graceful shutdown.
The limitation is equally important: complete HTTP/3 support requires more than this one patch. Additional Apache APIs, module compatibility work, QUIC libraries, TLS integration, build-system changes and extensive runtime testing are still necessary.
For organizations interested in experimenting with HTTP/3 on an Apache-based stack, a backport can be useful for development and validation. It should not yet be interpreted as a production-supported HTTP/3 upgrade for Apache 2.4.68.
What PR #699 does not provide
To understand the importance of the work without overstating it, several boundaries should be clear.
It does not implement QUIC
The patch does not provide:
- QUIC packet processing;
- congestion control;
- retransmission;
- QUIC stream management;
- TLS integration for QUIC;
- connection migration;
- HTTP/3 frame parsing.
Those functions belong to mod_http3 and its underlying QUIC and HTTP/3 libraries.
It does not add mod_http3 to stable Apache
Apache HTTP Server 2.4.68 still does not ship with a native mod_http3 module. The official stable module index includes mod_http2 but no bundled HTTP/3 equivalent.
It does not guarantee that HTTP/3 will be merged
PR #699 is open and targets Apache trunk. It has no published release milestone on the pull-request page. Even if the design is accepted, it may change during review before appearing in a released Apache version.
It does not make experimental software production-ready
The external mod_http3 implementation is still explicitly marked experimental. Production readiness requires substantially more than successful protocol negotiation in a test environment.
It does not provide full HTTP/3 observability
The MPM learns that an extra connection exists, but the proposed API does not expose details such as:
- QUIC connection identifiers;
- active stream counts;
- handshake state;
- packet loss;
- round-trip time;
- congestion windows;
- transport errors.
The interface provides lifecycle accounting, not complete monitoring.
Correct accounting is critical
The proposed API is simple, but it creates a strict responsibility for module authors.
Every successful call to:
ap_mpm_note_extra_connection_added()
must eventually have exactly one matching call to:
ap_mpm_note_extra_connection_removed()
Missing a removal could keep an Apache child alive indefinitely during a graceful shutdown.
Removing the same connection twice could corrupt the accounting and allow the child to exit too early.
The module must therefore connect these notifications to every possible connection termination path, including:
- normal remote close;
- local close;
- failed QUIC handshake;
- idle timeout;
- transport error;
- listener shutdown;
- child shutdown;
- memory allocation failure;
- partially initialized connection cleanup.
This is another reason why HTTP/3 support requires careful integration work rather than simply applying a patch and enabling a directive.
Why the change is larger than its code size
PR #699 is relatively small when measured in modified lines.
Architecturally, however, it changes a fundamental assumption:
Traditional Apache assumption:
Every connection that keeps a child alive is accepted and tracked
by the MPM.
New model enabled by PR #699:
The MPM owns the process lifecycle, but protocol modules may own
additional connection lifecycles and report them through a defined API.
That separation is necessary for QUIC.
HTTP/3 is not merely HTTP/2 operating on another socket type. QUIC moves substantial transport functionality from the kernel’s TCP implementation into a user-space protocol stack.
As a result, the web server must become more flexible about who creates a connection, who processes it and who knows when it is finished.
PR #699 is an early but meaningful adaptation of Apache’s architecture to that reality.
Conclusion
Apache HTTP Server still lacks the native, stable HTTP/3 support that many administrators have been waiting for.
Today, Apache users generally need an HTTP/3-capable CDN, load balancer or reverse proxy in front of Apache, or they must experiment with an external module built against Apache development code.
The experimental mod_http3 project shows that native HTTP/3 on Apache is becoming technically realistic. PR #699 addresses one of the core integration problems by allowing the event, worker and prefork MPMs to account for connections they did not accept themselves.
This should make it possible for module-owned QUIC connections to:
- keep Apache child processes alive while active;
- drain correctly during graceful reloads;
- avoid unsafe assumptions about MPM-private connection data;
- behave consistently across the primary Unix MPMs.
At CodeIT.guru, our attempt to backport this work to Apache 2.4.68 showed why the patch is important and why it is not sufficient on its own. The MPM changes can provide part of the required foundation, but complete HTTP/3 support still depends on additional trunk APIs, a mature HTTP/3 module and thorough production testing.
PR #699 should therefore be viewed neither as “HTTP/3 is now available” nor as an insignificant internal refactoring.
It is a promising foundational step toward a long-awaited result: allowing Apache HTTP Server itself to terminate and serve real HTTP/3 connections reliably, rather than depending entirely on another server at the network edge.
