Learn how to enable Gzip compression in Spring Boot based on specific conditions. Follow our detailed guide to optimize your application performance by configuring Gzip compression conditionally.
1. Introduction
In our previous article on Gzip compression in Spring Boot, we explained how to enable Gzip compression using simple properties in the application.properties
file. This method, however, enables Gzip compression globally. But what if we want to enable Gzip compression based on certain conditions, such as when the “Accept-Encoding” header contains “gzip” or only for specific URLs?
In this article, we will explore three ways to achieve conditional Gzip compression in Spring Boot:
- Configure exclude agents in application properties
- Use the “Accept-Encoding” header with a value of “identity”
- Implement a custom filter for Gzip compression
2. Method 1: Configure Exclude Agents in Application Properties
One way to control Gzip compression is by configuring excluded user agents in the application.properties
file. This allows us to selectively disable Gzip compression based on the user agent making the request.
Steps:
1. Add excluded user agents in application.properties
:
server.compression.enabled=true
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain,text/css,text/javascript,application/javascript
server.compression.min-response-size=2048
server.compression.excluded-user-agents=PostmanRuntime/7.39.0
application.properties2. Use the specified user agent while making API calls to prevent Gzip compression:
By configuring excluded agents, we can ensure that requests from specific user agents do not undergo Gzip compression, giving us finer control over which requests are compressed.
3. Method 2: Use the Accept-Encoding Header with Value Identity
Spring Boot enables Gzip compression globally when configured in application.properties
. However, we can bypass this compression by using the “Accept-Encoding” header with a value of “identity”. The “identity” value indicates that no transformation or encoding should be applied to the response.
Steps:
1. Enable Gzip compression globally in application.properties
:
server.compression.enabled=true
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain,text/css,text/javascript,application/javascript
server.compression.min-response-size=2048
application.properties2. Pass the “Accept-Encoding” header with value “identity” to skip Gzip compression:
By using the “Accept-Encoding: identity” header, we instruct the server not to apply any content encoding, effectively disabling Gzip compression for that particular request.
4. Method 3: Implement a Custom Filter for Conditional Gzip Compression
For more granular control over Gzip compression, we can implement a custom filter that selectively applies Gzip compression based on specific conditions, such as the URL pattern or the presence of the “Accept-Encoding” header.
Note: In this method, we will not enable Gzip compression globally via application.properties
. Instead, the custom filter will independently handle the compression logic.
Steps:
1. create a custom filter class using OncePerRequestFilter
:
This ensures that the filter is only applied once per request.
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.List;
@Component
public class GzipResponseCompressionFilter extends OncePerRequestFilter {
private static final List<String> EXCLUDE_GZIP_URLS = List.of("/exclude-compression");
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (acceptsGZipEncoding(request) && !EXCLUDE_GZIP_URLS.contains(request.getRequestURI())) {
GzipHttpServletResponseWrapper gzipResponse = new GzipHttpServletResponseWrapper(response);
gzipResponse.setHeader("Content-Encoding", "gzip");
filterChain.doFilter(request, gzipResponse);
gzipResponse.close();
} else {
filterChain.doFilter(request, response);
}
}
private boolean acceptsGZipEncoding(HttpServletRequest request) {
String acceptEncoding = request.getHeader("Accept-Encoding");
return acceptEncoding != null && acceptEncoding.contains("gzip");
}
}
GzipResponseCompressionFilter.javaExplanation:
- The
GzipResponseCompressionFilter
class extendsOncePerRequestFilter
, which ensures that the filter logic is executed only once per request. - The
doFilterInternal
method checks if the request supports Gzip encoding by examining the “Accept-Encoding” header and ensures the request URI is not in theEXCLUDE_GZIP_URLS
list. - If the conditions are met, it wraps the response in a
GzipHttpServletResponseWrapper
to handle Gzip compression. - If the conditions are not met, it proceeds with the normal filter chain.
2. Create a custom response wrapper class:
This class wraps the HTTP response to add Gzip compression.
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;
class GzipHttpServletResponseWrapper extends HttpServletResponseWrapper {
private final GZIPOutputStream gzipOutputStream;
private ServletOutputStream outputStream;
private PrintWriter writer;
public GzipHttpServletResponseWrapper(HttpServletResponse response) throws IOException {
super(response);
this.gzipOutputStream = new GZIPOutputStream(response.getOutputStream());
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (this.outputStream == null) {
this.outputStream = new GzipServletOutputStream(this.gzipOutputStream);
}
return this.outputStream;
}
@Override
public PrintWriter getWriter() throws IOException {
if (this.writer == null) {
this.writer = new PrintWriter(new OutputStreamWriter(this.gzipOutputStream, getCharacterEncoding()));
}
return this.writer;
}
@Override
public void flushBuffer() throws IOException {
if (this.writer != null) {
this.writer.flush();
}
if (this.outputStream != null) {
this.outputStream.flush();
}
this.gzipOutputStream.flush();
}
public void close() throws IOException {
this.gzipOutputStream.close();
}
}
GzipHttpServletResponseWrapper.javaExplanation:
- The
GzipHttpServletResponseWrapper
class extendsHttpServletResponseWrapper
to wrap the original response and add Gzip compression. - It overrides
getOutputStream
andgetWriter
to return Gzip-compressed output streams.ThegetOutputStream
method returns aGzipServletOutputStream
, which writes compressed data to theGZIPOutputStream
. - The
getWriter
method returns aPrintWriter
that writes to theGZIPOutputStream
. - The
flushBuffer
method ensures all data is written to the client before the response is flushed. - The
close
method closes theGZIPOutputStream
to complete the compression process.
3. Create a custom servlet output stream class:
This class handles writing the Gzip-compressed data to the output stream.
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.WriteListener;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
class GzipServletOutputStream extends ServletOutputStream {
private final GZIPOutputStream gzipOutputStream;
public GzipServletOutputStream(GZIPOutputStream gzipOutputStream) {
this.gzipOutputStream = gzipOutputStream;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
@Override
public void write(int b) throws IOException {
this.gzipOutputStream.write(b);
}
}
GzipServletOutputStream.javaExplanation:
- The
GzipServletOutputStream
class extendsServletOutputStream
and writes the compressed data to theGZIPOutputStream
. - The
write
method writes data to theGZIPOutputStream
.
How It All Works Together
When a request is received, the GzipResponseCompressionFilter
checks if the “Accept-Encoding” header contains “gzip” and ensures the request URI is not in the EXCLUDE_GZIP_URLS
list. If both conditions are met, the filter wraps the HttpServletResponse
in a GzipHttpServletResponseWrapper
, which in turn wraps the output stream in a GZIPOutputStream
. This setup ensures that all data written to the response is compressed using Gzip before being sent to the client.
The CustomHttpServletResponseWrapper
class manages the Gzip compression by wrapping the response’s output stream. It overrides the getOutputStream
and getWriter
methods to return Gzip-compressed streams, ensuring that all data sent to the client is compressed. The GzipServletOutputStream
class facilitates writing the compressed data to the GZIPOutputStream
.
Output:
By implementing this custom filter, we can selectively apply Gzip compression based on specific conditions, such as the presence of the “Accept-Encoding” header or certain URL patterns. This provides a more flexible and efficient way to optimize our application’s performance.
5. Source Code
The complete source code of the above example to perform conditional gzip compression can be found here.
6. FAQs
Why enable Gzip compression conditionally?
Conditional Gzip compression allows you to apply compression only under certain conditions, such as specific URLs or when the client supports Gzip encoding. This can help optimize performance and reduce unnecessary processing.
How to exclude certain URLs from Gzip compression in Spring Boot?
You can exclude certain URLs by implementing a custom filter. The filter checks the request URI and decides whether to apply Gzip compression based on predefined conditions.
What does the Accept-Encoding identity header do?
The Accept-Encoding: identity header tells the server not to apply any content encoding to the response. This effectively disables Gzip compression for that particular request.
How to verify if Gzip compression is working correctly?
You can use browser developer tools to check if responses from your server are compressed. Look for the “Content-Encoding: gzip” header in the response.
What is the difference between identity and gzip values in the Accept-Encoding header?
The gzip value indicates that the client can accept Gzip-compressed responses, while the identity value indicates that the response should not be encoded and should be sent as-is, without any compression.
7. Conclusion
By following these methods, you can enable Gzip compression for specific conditions in your Spring Boot application. Whether you choose to configure excluded user agents, use the “Accept-Encoding” header, or implement a custom filter, each approach provides you with a way to optimize your application’s performance while maintaining control over Gzip compression. Utilize these techniques to ensure your application delivers compressed responses efficiently, improving load times and user experience.
8. Learn More
Interested in learning more?
Checkout our blog on How to Enable Gzip Compression in Spring Boot
Add a Comment