skip to content

Using ConfigurableServletWebServerFactory, how do you programmatically set custom error pages and enable response compression?

level: middleimportance: should knowfreq 30%

answer

  1. addErrorPages = additive; setErrorPages/setCompression = replace
  2. ErrorPage maps status OR exception -> path
  3. Compression: enabled, minResponseSize, mimeTypes
  4. methods live on ConfigurableWebServerFactory / ErrorPageRegistry
  5. container-agnostic (Tomcat/Jetty/Undertow)

basics

~10 s

In a WebServerFactoryCustomizer, call factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404")) for error pages, and build a Compression object (setEnabled(true), mime types, min size) and pass it to factory.setCompression(compression).

solid answer

~30 s

Register a WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>. For error pages call factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404")) or an exception-based ErrorPage; the path is a servlet path Boot forwards to on that status. For compression, create an org.springframework.boot.web.server.Compression, setEnabled(true), optionally setMinResponseSize(DataSize), setMimeTypes(...), and setExcludedUserAgents(...), then factory.setCompression(compression). Both of these are equally expressible as server.error.* and server.compression.* properties, so in practice you'd only do it in code when the values are dynamic. Note addErrorPages is additive (it appends), while setCompression and setErrorPages replace. These settings live on ConfigurableWebServerFactory / ErrorPageRegistry, so they work across Tomcat, Jetty and Undertow — no container-specific API needed.

code

java · 23 lines
java
import org.springframework.boot.web.server.*;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.unit.DataSize;

@Component
class ErrorAndCompressionCustomizer
        implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
        factory.addErrorPages(
            new ErrorPage(HttpStatus.NOT_FOUND, "/errors/404"),
            new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/errors/500"));

        Compression compression = new Compression();
        compression.setEnabled(true);
        compression.setMinResponseSize(DataSize.ofKilobytes(2));
        compression.setMimeTypes(new String[] {"text/html", "application/json"});
        factory.setCompression(compression);
    }
}

go deeper

for a junior

Should know these settings exist and are usually done via server.error.* / server.compression.* properties.

for a middle

Should name ErrorPage and Compression and call the right factory setters, knowing the methods are container-agnostic.

for a senior

Should distinguish additive addErrorPages from replacing setters and note the minResponseSize/MIME gotchas.

for a principal

Should reason about interaction of multiple customizers and when code beats properties for these dynamic knobs.

## Where these methods live The factory type you customize for a servlet app is `ConfigurableServletWebServerFactory`. Its supertypes carry the relevant methods: - `ConfigurableWebServerFactory` → `setPort`, `setAddress`, `setSsl`, `setHttp2`, `setCompression`, `setServerHeader`, `setShutdown`. - `ErrorPageRegistry` (a supertype) → `addErrorPages(ErrorPage...)`. - `ConfigurableServletWebServerFactory` itself → servlet extras like `setContextPath`, `setSession`, `setMimeMappings`, `setRegisterDefaultServlet`. Because compression and error pages are defined on the **container-agnostic** interfaces, the same code works whether the embedded server is Tomcat, Jetty, or Undertow. ## Error pages `org.springframework.boot.web.server.ErrorPage` maps either an **HTTP status** or an **exception type** to a **path** that Boot forwards to: ```java factory.addErrorPages( new ErrorPage(HttpStatus.NOT_FOUND, "/errors/404"), new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/errors/500"), new ErrorPage(NullPointerException.class, "/errors/npe") ); ``` The path is an application path handled by one of your controllers/views. `addErrorPages` is **additive** — each call appends to the registry. There is also `setErrorPages(Set)` which **replaces** the whole set. Note: for typical Boot apps the `BasicErrorController` + `server.error.path` already provide a `/error` mapping and a whitelabel page; custom `ErrorPage`s are for overriding specific statuses/exceptions. ## Compression `org.springframework.boot.web.server.Compression` is a plain config holder: ```java Compression compression = new Compression(); compression.setEnabled(true); compression.setMinResponseSize(DataSize.ofKilobytes(2)); compression.setMimeTypes(new String[] { "text/html", "text/css", "application/json" }); compression.setExcludedUserAgents(new String[] { "legacy-agent" }); factory.setCompression(compression); ``` - `minResponseSize` (a `DataSize`): responses smaller than this are not compressed (default 2KB). - `mimeTypes`: which content types to compress. - Compression is performed by the container (Tomcat's gzip), so the actual mechanism is delegated; the `Compression` object just carries settings the factory translates into container config. ## Property equivalents Both features have full property support: `server.error.include-message`, custom pages via templates in `src/main/resources/public/error/`, and `server.compression.enabled`, `server.compression.min-response-size`, `server.compression.mime-types`. So do this in code only when the values must be computed, or when combining with other programmatic tweaks. ## Gotchas - **Additive vs replace**: `addErrorPages` appends; calling it multiple times (or from multiple customizers) accumulates. `setCompression` overwrites — a later customizer wins. - Compression won't kick in for responses below `minResponseSize` or for non-listed MIME types — a frequent 'why isn't gzip working' surprise. - Setting `setCompression(null)` disables it; passing an object with `enabled=false` also disables.

  • Your gzip compression seems to be ignored for small JSON responses. Why?
    Compression only applies to responses whose size is at least minResponseSize (default 2KB) and whose Content-Type is in the configured mimeTypes list. Small responses or unlisted content types are sent uncompressed by design.
  • If two customizers both set compression, which wins?
    setCompression replaces the whole object, so the customizer that runs last wins. Ordering (Ordered/@Order) determines that. addErrorPages, by contrast, is additive, so both customizers' error pages are kept.

saying these in an interview costs you the question

  • Thinking addErrorPages replaces the existing set rather than appending
  • Expecting compression to apply to every response regardless of size or MIME type
  • Believing error pages/compression require container-specific (Tomcat) APIs

context