Enabling HTTP/2 in Apache Tomcat with the NIO2 Connector: A Practical Guide
HTTP/1.1 forces browsers to open multiple TCP connections and suffer head-of-line blocking. Tomcat's NIO2 connector can multiplex HTTP/2 streams over one TLS connection—if you configure it correctly.
07 Jul 2026, 14:39 UTC

The bottleneck you can actually fix
If you run a Tomcat server that serves both HTML pages and JSON APIs, you have probably watched browsers open six or more TCP connections just to render one page. Under HTTP/1.1, each connection carries one request at a time, and a slow response can block the ones behind it—a problem called head-of-line blocking. The useful takeaway is that Tomcat's NIO2 connector can negotiate HTTP/2, multiplexing many request/response streams over a single TLS connection and removing that per-connection bottleneck.
Why HTTP/1.1 hurts modern workloads
HTTP/1.1 was designed for a web of documents, not for the asset-heavy, API-driven pages common today. Browsers work around the one-request-per-connection limit by opening parallel connections, but that approach hits per-host connection caps and adds TLS handshake overhead. When a large response occupies a connection, smaller requests wait behind it even though the server could handle them concurrently.
HTTP/2 changes that model. It frames requests and responses into independent streams that share one connection. The server can interleave chunks from multiple streams, so a slow image download no longer blocks an API call. In Tomcat, the only connector that speaks HTTP/2 natively is the NIO2 connector.
What you need before you flip the switch
HTTP/2 support in Tomcat is not a drop-in flag. It depends on Java and TLS capabilities that must be present before the connector starts.
- Java 11 or later. Earlier JDKs lack the ALPN APIs needed to negotiate HTTP/2 during the TLS handshake.
- An ALPN-capable TLS provider. Tomcat can use the JDK's built-in TLS implementation or an external provider such as OpenSSL. If neither is available, the connector cannot offer HTTP/2.
- A valid certificate and keyStore. Current Tomcat releases require TLS for HTTP/2. Without a properly configured keyStore, the connector will start but silently fall back to HTTP/1.1.
- Tomcat 9.0.76+ (or a current stable 10.x/11.x release). The NIO2 connector carried an experimental label in some earlier distributions; use an official, stable build.
ALPN, or Application-Layer Protocol Negotiation, is a TLS extension that lets the client and server agree on HTTP/2 before any application data is sent. Without it, a modern browser will simply use HTTP/1.1.
Worked example: server.xml
Assume Tomcat is installed at /opt/tomcat and you have a PKCS#12 keystore at /opt/tomcat/conf/keystore.p12. Edit /opt/tomcat/conf/server.xml as the user that owns the Tomcat process (or with sudo if required). Back up the file first.
<Connector port="8443"
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
maxThreads="200"
SSLEnabled="true"
scheme="https"
secure="true">
<UpgradeProtocol
className="org.apache.coyote.http2.Http2Protocol"
compress="true" />
<SSLHostConfig
certificateVerification="none"
truststoreFile="conf/truststore.p12"
truststorePassword="changeit">
<Certificate
certificateKeystoreFile="conf/keystore.p12"
certificateKeystorePassword="changeit"
certificateKeystoreType="PKCS12"
certificateKeyAlias="tomcat" />
</SSLHostConfig>
</Connector>
Replace changeit and the file paths with your real values. The Http2Protocol upgrade element tells the NIO2 connector to advertise HTTP/2 via ALPN. If you omit it, or if the TLS stack cannot negotiate ALPN, the connector still serves HTTPS—just over HTTP/1.1.
How to verify it actually works
Silent fallback is the most common failure mode. Run these checks from a machine that can reach the server.
| Check | Command / Location | What to look for |
|---|---|---|
| Protocol negotiation | curl -I --http2 https://your-server.com:8443/ | The status line should read HTTP/2.0 200 (or another 2xx/3xx code). If it shows HTTP/1.1 200, ALPN did not select HTTP/2. |
| ALPN support | openssl s_client -alpn h2 -connect your-server.com:8443 | In the handshake output, look for ALPN protocol: h2. If the server returns no ALPN extension, the TLS provider is missing or misconfigured. |
| Tomcat logs | tail -f /opt/tomcat/logs/catalina.out | After startup, look for messages confirming HTTP/2 is enabled for the connector. Also grep for warnings containing HTTP/2 not supported; those indicate the upgrade path was rejected. |
If you see HTTP/1.1 in curl but no obvious error in the logs, check that your JDK is 11+ and that the keystore password and alias match the files on disk.
Trade-offs and limitations
HTTP/2 is not free. Each active stream consumes memory for framing, flow control, and priority state. On a high-concurrency server with thousands of simultaneous streams, that per-stream overhead can increase garbage-collector pressure. If you notice throughput dropping after the switch, profile heap usage and consider tuning the JVM garbage collector or lowering maxConcurrentStreams on the connector.
There is also a compatibility floor. Very old browsers—Internet Explorer 11, for example—do not speak HTTP/2. Tomcat handles this gracefully by falling back to HTTP/1.1 for those clients, but you should not expect uniform latency improvements across every user agent.
Finally, remember that HTTP/2 in Tomcat is TLS-only in current releases. If your architecture requires cleartext HTTP/2 (h2c), you will need an upstream proxy or a different server, because the Tomcat connector will not negotiate it.
Next steps
Before you change production, copy server.xml to a safe location. Apply the connector configuration in a staging environment, restart Tomcat, and run the three verification steps above. Only promote the change after curl reports HTTP/2.0 and catalina.out is free of HTTP/2 not supported warnings. If throughput regresses, revert to the backup and revisit JVM tuning before trying again.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.