55

Tomcat 8.5, which will be the default in Spring Boot 1.4, supports HTTP/2.

How can HTTP/2 be enabled in a Spring Boot application?

Mahozad
  • 18,032
  • 13
  • 118
  • 133
yglodt
  • 13,807
  • 14
  • 91
  • 127

5 Answers5

60

In Spring Boot 2.1 and above it is as simple as adding this property to your .properties (or .yml) file:

server.http2.enabled=true

You can also do it programmatically like this (in one of your configuration classes):

@Bean
public ConfigurableServletWebServerFactory tomcatCustomizer() {
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    factory.addConnectorCustomizers(connector -> connector.addUpgradeProtocol(new Http2Protocol()));
    return factory;
}
Mahozad
  • 18,032
  • 13
  • 118
  • 133
  • 6
    this only works when JDK 9 or above is used. I have been trying to enable it for my spring boot service running in a docker container but it does not work . Check here https://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-web-servers.html – Avijeet May 02 '19 at 10:43
  • It just enables HTTP2, but any configuration in application.yml is not passed to Http2Protocol. I could not find any other solution than java self implemented config, that forwards server.* properties to Http2Protocol. – sgflt Oct 14 '21 at 05:11
37

You need to add the HTTP 2 upgrade protocol to Tomcat's connector. You can do that by customizing the embedded Tomcat container:

Java 8:

@Bean
public EmbeddedServletContainerCustomizer tomcatCustomizer() {
    return (container) -> {
        if (container instanceof TomcatEmbeddedServletContainerFactory) {
            ((TomcatEmbeddedServletContainerFactory) container)
                    .addConnectorCustomizers((connector) -> {
                connector.addUpgradeProtocol(new Http2Protocol());
            });
        }
    };
}

Java 7:

@Bean
public EmbeddedServletContainerCustomizer tomcatCustomizer() {
    return new EmbeddedServletContainerCustomizer() {

        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            if (container instanceof TomcatEmbeddedServletContainerFactory) {
                ((TomcatEmbeddedServletContainerFactory) container)
                        .addConnectorCustomizers(new TomcatConnectorCustomizer() {
                    @Override
                    public void customize(Connector connector) {
                        connector.addUpgradeProtocol(new Http2Protocol());
                    }

                });
            }
        }

    };
}
Andy Wilkinson
  • 108,729
  • 24
  • 257
  • 242
  • 2
    Is there anything else that needs to be done apart of running Tomcat with https? (I did enable it for localhost by following this question http://stackoverflow.com/questions/30404579/how-to-set-up-ssl-tls-https-on-spring-boot-using-aes-256). But in Chrome's network debugger I see everything is still served via http/1.1 – yglodt Jul 27 '16 at 20:00
  • 1
    `h2c` will work as-is but not many (any?) browsers support it. If you want it to work securely (`h2`), you'll need to jump through some hoops with Tomcat Native. – Andy Wilkinson Jul 27 '16 at 20:48
  • 24
    That's a strange way to ask for more help. I'm done here. – Andy Wilkinson Jul 27 '16 at 21:38
  • 2
    Just to add that Tomcat 9 supports HTTP2 with a few configuration in the server.xml file. https://readlearncode.com/configure-tomcat-9-for-http2/ – Alex Theedom Aug 10 '16 at 06:50
  • I have a potentially very dumb question. Several times, I have seen "include this bean" in relation to Spring Boot ... but I have not been able to figure out what file to open/create before I paste the bean. – elyograg Sep 08 '16 at 12:45
  • 1
    Just add snippet above in a class annotated with @Configuration of spring framework – Andreas Falk Nov 08 '16 at 15:38
  • @AndyWilkinson i tried your solution and subsequently added a new TLS 1.2 self signed certificate but still my browser is using http 1.1. Is there a problem with embedded tomcat? – sam Dec 04 '18 at 07:08
  • 2
    I can’t say without some more information. Why not add a question of your own, providing as much information as you can? – Andy Wilkinson Dec 04 '18 at 07:49
26

The most elegant and best-performing way to enable HTTP/2 with a Spring Boot application follows here.

First, as mentioned in Andy Wilkinson's answer, you need to enable HTTP/2 at Tomcat level:

@Bean
public EmbeddedServletContainerCustomizer tomcatCustomizer() {
    return (container) -> {
        if (container instanceof TomcatEmbeddedServletContainerFactory) {
            ((TomcatEmbeddedServletContainerFactory) container)
                    .addConnectorCustomizers((connector) -> {
                connector.addUpgradeProtocol(new Http2Protocol());
            });
        }
    };
}

In case you are not using an embedded Tomcat, you can set up HTTP/2 listening like this:

<Connector port="5080" protocol="HTTP/1.1" connectionTimeout="20000">
    <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
</Connector>

Remember that you need Tomcat >= 8.5.

Then, you should use HAProxy (version >= 1.7) in front of Tomcat to take care of encryption.

The client will speak https to HAProxy, and HAProxy will speak cleartext HTTP/1.1 or HTTP/2 to the backend, as the client requested. There will be no unnecessary protocol translations.

The matching HAProxy-configuration is here:

# Create PEM: cat cert.crt cert.key ca.crt > /etc/ssl/certs/cert.pem

global
    tune.ssl.default-dh-param 2048
    ssl-default-bind-options no-sslv3 no-tls-tickets force-tlsv12
    ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
    chroot /var/lib/haproxy
    user haproxy
    group haproxy

defaults
    timeout connect 10000ms
    timeout client 60000ms
    timeout server 60000ms

frontend fe_https
    mode tcp
    rspadd Strict-Transport-Security:\ max-age=31536000;\ includeSubDomains;\ preload
    rspadd X-Frame-Options:\ DENY
    bind *:443 ssl crt /etc/ssl/certs/cert.pem alpn h2,http/1.1
    default_backend be_http

backend be_http
    mode tcp
    server domain 127.0.0.1:8080
    # compression algo gzip # does not work in mode "tcp"
    # compression type text/html text/css text/javascript application/json

Edit 2019

I face two problems when using mode "tcp"

  • Compression does not work, since it depends on mode http. So the backend has to take care of it
  • The backend can not see the client's IP-address. Probably I need NAT. Still investigating...

Generally, since haproxy proxies a lower level tcp connection, there is no access to any http stuff

yglodt
  • 13,807
  • 14
  • 91
  • 127
  • 1
    Is there a way to not use HAproxy? Spring boot and tomcat couldn't just handle encription and the upgrade? – corlaez Aug 01 '17 at 20:08
  • Check this link https://readlearncode.com/configure-tomcat-9-for-http2/ posted by Alex above. – yglodt Aug 01 '17 at 21:40
  • I was planning to use it with embedded tomcat of spring which is 8.5. I think I will try Undertow it is supposed to have support for http2 – corlaez Aug 01 '17 at 21:50
  • 2
    Embedded Tomcat does also support http2, and ssl can be configured like this: https://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-servlet-containers.html#howto-configure-ssl. If you do that, and add the connector.addUpgradeProtocol(new Http2Protocol()); I believed it should work. – yglodt Aug 02 '17 at 06:00
  • Do you have source about embedded tomcat (8.5) support of http2? I read something about tomcat native. I havent been able to set up tomcat yet. – corlaez Aug 03 '17 at 04:07
  • I have checked with spring boot 2.2.4, embedded tomcat 9.x and JAVA8. By configuring server.http2.enabled=true and setting up the java.library.path to tomcat native bin path, I am able to see the APIs running on HTTP2. – theNextBigThing Feb 18 '20 at 05:46
15

In Spring Boot 2 you first need a certificate - it can by generated like this:

keytool -genkey -keyalg RSA -alias my-the-best-api -keystore c:\tmp\keystore.store -storepass secret -validity 3650 -keysize 2048

Than you just need to add this certificate to classpath and add needed properties to application.properties:

server.http2.enabled=true
server.port = 8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
Mohit Kanwar
  • 2,962
  • 7
  • 39
  • 59
Jozef Najman
  • 171
  • 1
  • 4
  • Worth noting that Spring Boot 2.0.x uses Tomcat 8.5, so if using embedded Tomcat, you still need to configure the path to `libtcnative` as noted in the [Spring Boot docs](https://docs.spring.io/spring-boot/docs/2.0.2.RELEASE/reference/html/howto-embedded-web-servers.html#howto-configure-http2-tomcat). Spring Boot 2.1.x will ship with Tomcat 9. – daiscog Oct 22 '18 at 09:13
7

Spring Boot 2.2.0+ ships by default with Tomcat 9.0.x which supports HTTP/2 out of the box when using JDK 9 or later. Link

usertest
  • 2,140
  • 4
  • 32
  • 51