118

We use Spring Boot/MVC with annotation-based java-config for series of RESTful services and we want to selectively enable HTTP GZIP stream compression on some API responses.

I know I can do this manually in my controller and a byte[] @ResponseBody, however we'd prefer to rely on the Spring MVC infrastructure (filters/etc) and have it automatically do the JSON conversion and compression (i.e. the method returns a POJO).

How can I enable GZIP compression in the ResponseBody or embedded Tomcat instance, and in a way we can selectively compress only some responses?

We don't currently have any XML based configuration.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
user3182614
  • 1,241
  • 2
  • 10
  • 5

8 Answers8

223

The rest of these answers are out of date and/or over the top complicated for something that should be simple IMO (how long has gzip been around for now? longer than Java...) From the docs:

In application.properties 1.3+

# ️️️
server.compression.enabled=true
# opt in to content types
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css
# not worth the CPU cycles at some point, probably
server.compression.min-response-size=10240 

In application.properties 1.2.2 - <1.3

server.tomcat.compression=on
server.tomcat.compressableMimeTypes=application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css

Older than 1.2.2:

@Component
public class TomcatCustomizer implements TomcatConnectorCustomizer {

  @Override
  public void customize(Connector connector) {
    connector.setProperty("compression", "on");
    // Add json and xml mime types, as they're not in the mimetype list by default
    connector.setProperty("compressableMimeType", "text/html,text/xml,text/plain,application/json,application/xml");
  }
}

Also note this will ONLY work if you are running embedded tomcat:

If you plan to deploy to a non embedded tomcat you will have to enable it in server.xml http://tomcat.apache.org/tomcat-9.0-doc/config/http.html#Standard_Implementation

IRL Production Note:

Also to avoid all of this consider using a proxy/load balancer setup in front of Tomcat with nginx and/or haproxy or similar since it will handle static assets and gzip MUCH more efficiently and easily than Java/Tomcat's threading model.

You don't want to throw 'cat in the bath because it's busy compressing stuff instead of serving up requests (or more likely spinning up threads/eating CPU/heap sitting around waiting for database IO to occur while running up your AWS bill which is why traditional Java/Tomcat might not be a good idea to begin with depending on what you are doing but I digress...)

refs: https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/howto.html#how-to-enable-http-response-compression

https://github.com/spring-projects/spring-boot/issues/2031

John Culviner
  • 22,235
  • 6
  • 55
  • 51
  • Your approach for versions old than 1.2.2 won't work as Spring Boot doesn't look for `TomcatConnectorCustomizer` instances in the application context; they have to be programatically registered with `TomcatEmbeddedServletContainerFactory` – Andy Wilkinson Feb 19 '15 at 08:59
  • Thanks for the heads up. I ended up giving up on this since it seems static/dynamic/tomcat/vs boot was still an issue. This is way harder than it should be... Nginx reverse proxy FTW! – John Culviner Feb 19 '15 at 23:42
  • 3
    In SpringBoot, the new properties are server.compression.enabled=true and server.compression.mime-types=XXX,YYY https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.3.0-Full-Release-Notes – blacelle Nov 14 '15 at 14:45
  • I'm not using spring boot. How can I configure this in embedded jetty? – Lucky Aug 09 '16 at 06:34
  • 2
    If for spring boot we have multiple rest controllers all returning JSON responses. Can we selectively apply the zip on some controllers? –  Oct 12 '16 at 19:10
  • Can we set this through JMX? – Perimosh Feb 24 '17 at 18:07
  • I have tried this but not working, please take a look at https://stackoverflow.com/questions/44309838/spring-boot-request-response-compression – Bhargav Jun 01 '17 at 14:29
  • how do we verify this compression? – Bhargav Jul 27 '17 at 10:24
  • why no mime for js & css? – goat Jan 11 '18 at 08:12
  • You may not need to set mime-types, because the current (Spring Boot 2) default is: "text/html", "text/xml", "text/plain","text/css", "text/javascript", "application/javascript","application/json","application/xml" – RiZKiT Oct 25 '18 at 09:06
  • 4
    You should also mention the minimum response size for compression (ex: 10KB) otherwise it becomes overhead for the server to compress every (ex: 0.5KB) request. `server.compression.min-response-size=10240` – UsamaAmjad Feb 15 '19 at 12:58
  • This works (1.3+) with all embedded containers, not only Tomcat. – OrangeDog Aug 19 '19 at 15:00
  • Be aware of [BREACH](https://en.wikipedia.org/wiki/BREACH) when configuring the mime-types. In particular compressing `text/html` may open you to attacks on CSRF tokens and such. – OrangeDog Aug 19 '19 at 15:03
  • I have not been able to find an answer and might sound like stupid question but, if I enable this and communicate with another SpringBoot app, it will automatically descompress the gziped json right? – BugsOverflow Feb 26 '23 at 09:01
  • Not unless you implement IGzipAbstractFactoryResolverInterfaceDecoratorResolverProxyFacade to handle it. JK. No idea :) – John Culviner Mar 01 '23 at 21:06
17

On recents versions in application.yml config:

---

spring:
  profiles: dev

server:
  compression:
    enabled: true
    mime-types: text/html,text/css,application/javascript,application/json

---
M. Reza Nasirloo
  • 16,434
  • 2
  • 29
  • 41
13

This is basically the same solution as @andy-wilkinson provided, but as of Spring Boot 1.0 the customize(...) method has a ConfigurableEmbeddedServletContainer parameter.

Another thing that is worth mentioning is that Tomcat only compresses content types of text/html, text/xml and text/plain by default. Below is an example that supports compression of application/json as well:

@Bean
public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
    return new EmbeddedServletContainerCustomizer() {
        @Override
        public void customize(ConfigurableEmbeddedServletContainer servletContainer) {
            ((TomcatEmbeddedServletContainerFactory) servletContainer).addConnectorCustomizers(
                    new TomcatConnectorCustomizer() {
                        @Override
                        public void customize(Connector connector) {
                            AbstractHttp11Protocol httpProtocol = (AbstractHttp11Protocol) connector.getProtocolHandler();
                            httpProtocol.setCompression("on");
                            httpProtocol.setCompressionMinSize(256);
                            String mimeTypes = httpProtocol.getCompressableMimeTypes();
                            String mimeTypesWithJson = mimeTypes + "," + MediaType.APPLICATION_JSON_VALUE;
                            httpProtocol.setCompressableMimeTypes(mimeTypesWithJson);
                        }
                    }
            );
        }
    };
}
matsev
  • 32,104
  • 16
  • 121
  • 156
  • I tried adding this into my Java Configuration and found that the compression didn't seem to be operating at all. I am using Spring Boot with Tomcat as the embedded container, and wondered if there were any additional things I needed to set other than this configuration? – Michael Coxon Jul 23 '14 at 04:41
  • 2
    Try verifying by specifying the `Accept-Encoding: gzip,deflate` header, if you are using curl: `curl -i -H 'Accept-Encoding: gzip,deflate' http://url.to.your.server` – matsev Jul 23 '14 at 19:57
11

I have added for this:

Server compression

server.compression.enabled=true
server.compression.min-response-size=2048
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain

taken from http://bisaga.com/blog/programming/web-compression-on-spring-boot-application/

Oleksii Kyslytsyn
  • 2,458
  • 2
  • 27
  • 43
10

Spring Boot 1.4 Use this for Javascript HTML Json all compressions.

server.compression.enabled: true
server.compression.mime-types: application/json,application/xml,text/html,text/xml,text/plain,text/css,application/javascript
Ronny Shibley
  • 2,030
  • 22
  • 25
6

Enabeling GZip in Tomcat doesn't worked in my Spring Boot Project. I used CompressingFilter found here.

@Bean
public Filter compressingFilter() {
    CompressingFilter compressingFilter = new CompressingFilter();
    return compressingFilter;
}
user1127860
  • 150
  • 2
  • 14
  • @user1127860 tnx this works but anyway to configure this Filter further? I use spring boot and cant seem to add init params as manual says in web.xml – Spring Jun 12 '18 at 09:38
5

To enable GZIP compression, you need to modify the configuration of the embedded Tomcat instance. To do so, you declare a EmbeddedServletContainerCustomizer bean in your Java configuration and then register a TomcatConnectorCustomizer with it.

For example:

@Bean
public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
    return new EmbeddedServletContainerCustomizer() {
        @Override
        public void customize(ConfigurableEmbeddedServletContainerFactory factory) {
            ((TomcatEmbeddedServletContainerFactory) factory).addConnectorCustomizers(new TomcatConnectorCustomizer() {
                @Override
                public void customize(Connector connector) {
                    AbstractHttp11Protocol httpProtocol = (AbstractHttp11Protocol) connector.getProtocolHandler();
                    httpProtocol.setCompression("on");
                    httpProtocol.setCompressionMinSize(64);
                }
            });
        }
    };
}

See the Tomcat documentation for more details on the various compression configuration options that are available.

You say that you want to selectively enable compression. Depending on your selection criteria, then the above approach may be sufficient. It enables you to control compression by the request's user-agent, the response's size, and the response's mime type.

If this doesn't meet your needs then I believe you will have to perform the compression in your controller and return a byte[] response with a gzip content-encoding header.

Andy Wilkinson
  • 108,729
  • 24
  • 257
  • 242
  • 2
    what is the diffrent between your answer to the option to put the setting on application.properties ? server.compression.enabled=true server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css – lukass77 Feb 22 '18 at 10:13
  • 1
    This answer was written before properties-based compression configuration was available. They are equivalent, but the properties-based approach is easier so I would recommend using that. – Andy Wilkinson Feb 22 '18 at 21:26
  • just want to share that in my case tomcat is behind a load balancer that get https and forword the request to tomcat as http ., when I use application.properties solution response is not gzip but when I use the programmatic config solution on connector i get gzip response with https request LB – lukass77 Feb 23 '18 at 00:58
  • another question in case I use application.properties solution .. and define more 2 connectors on 8081 and 8082 ports .. does compresion applay to all conectors or just to the 8080 connector ? – lukass77 Feb 23 '18 at 01:02
  • i verfiy this , compersion is only apply on port 8080 , even if you open more connectors .. , i think bug should be open on this to spring boot ??.. , so only work solution for me was programmatic config for each connector , not application.properties – lukass77 Feb 23 '18 at 15:10
2

I had the same problem into my Spring Boot+Spring Data project when invoking to a @RepositoryRestResource.

The problem is the MIME type returned; which is application/hal+json. Adding it to the server.compression.mime-types property solved this problem for me.

Hope this helps to someone else!

Ari
  • 53
  • 2
  • 9