Using GZIP compression with Spring Boot/MVC/JavaConfig with RESTful
Asked Answered
P

9

121

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.

Perigee answered 28/1, 2014 at 15:49 Comment(3)
You should check out GzipFilter. – Enteric
don't use HTTP compression with HTTPS unless you know what you're doing – Segmentation
The gzip compression is disabled by default. Check this to enable gzip compression – Very
F
227

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

Fighterbomber answered 29/1, 2015 at 14:20 Comment(15)
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 – Abacus
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! – Fighterbomber
In SpringBoot, the new properties are server.compression.enabled=true and server.compression.mime-types=XXX,YYY github.com/spring-projects/spring-boot/wiki/… – Barbosa
I'm not using spring boot. How can I configure this in embedded jetty? – Mackmackay
If for spring boot we have multiple rest controllers all returning JSON responses. Can we selectively apply the zip on some controllers? – Khalid
Can we set this through JMX? – Annunciata
I have tried this but not working, please take a look at #44310338 – Acquaint
how do we verify this compression? – Acquaint
why no mime for js & css? – Cymbal
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" – Slugabed
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 – Finney
This works (1.3+) with all embedded containers, not only Tomcat. – Roundly
Be aware of BREACH when configuring the mime-types. In particular compressing text/html may open you to attacks on CSRF tokens and such. – Roundly
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? – Amargo
Not unless you implement IGzipAbstractFactoryResolverInterfaceDecoratorResolverProxyFacade<T, U, V> to handle it. JK. No idea :) – Fighterbomber
J
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

---
Jameejamel answered 15/7, 2016 at 14:38 Comment(2)
The corresponding bug report and fix where this was changed is github.com/spring-projects/spring-boot/issues/2737 – Tick
Can you use wildcard mime types? i.e image/*? It does not seem to work – Repertoire
T
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);
                        }
                    }
            );
        }
    };
}
Than answered 17/4, 2014 at 11:2 Comment(2)
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? – Embank
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 – Than
S
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/

Stanleigh answered 5/3, 2018 at 8:15 Comment(0)
E
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
Earthbound answered 25/10, 2016 at 11:45 Comment(2)
How do we verify this compression? – Acquaint
@Acquaint See the response header of your api response. It should contain the header:Content-Encoding : gzip – Indeed
S
7

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;
}
Sorayasorb answered 21/9, 2014 at 10:18 Comment(1)
@Sorayasorb 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 – Oliviaolivie
A
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.

Abacus answered 29/1, 2014 at 14:48 Comment(5)
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 – Roving
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. – Abacus
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 – Roving
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 ? – Roving
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 – Roving
A
3

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!

Aba answered 7/11, 2017 at 8:54 Comment(0)
U
0

Add following dependency in pom.xml

<dependency>
    <groupId>net.sourceforge.pjl-comp-filter</groupId>
    <artifactId>pjl-comp-filter</artifactId>
    <version>1.6.4</version>
</dependency>

Add following bean configuration into your configuration file.

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.DispatcherServlet;
import com.planetj.servlet.filter.compression.CompressingFilter;

@Bean
public FilterRegistrationBean<CompressingFilter> dispatcherRegistration(DispatcherServlet dispatcherServlet) {
    FilterRegistrationBean<CompressingFilter> registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(new CompressingFilter());
    registrationBean.setEnabled(true);
    registrationBean.addUrlPatterns("/*");
    return registrationBean;
}

CompressingFilter helps to compress the response.

You can set GZIP compression of selective endpoints using addUrlPatterns(String) method in FilterRegistrationBean. Use setUrlPatterns(Collection<String>) method in case you have multiple endpoints.

If no URL pattern is set, filter compresses all responses.

Note: this won't even need to add any properties in application.properties file.

Unijugate answered 22/3 at 5:17 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.