Spring Boot is powerful because it gives you sensible defaults while still exposing almost every important web-server setting through configuration.
For a simple application, you can start it and accept the defaults. In production, however, you will usually want to configure at least some combination of the server port, context path, HTTP compression, HTTP/2, TLS, caching, multipart upload limits, proxy headers, request limits and graceful shutdown.
This guide covers the Spring Boot server configuration settings that are most useful in real applications, with examples for modern Spring Boot 4.x and notes where Spring Boot 3.x behaves differently.
Version note: This article has been updated for modern Spring Boot, including Spring Boot 4.x. Some property names and supported embedded servers have changed since the original Spring Boot 2.x version of this tutorial.
A normal Spring Boot web application does not require you to install Tomcat separately. The web server is embedded in the application and starts together with your Spring Boot process.
For servlet-based Spring MVC applications, Tomcat is the usual default. Jetty is another supported option. Reactive Spring WebFlux applications normally use Reactor Netty.
This means a Spring Boot application can normally be packaged as an executable JAR and started with:
java -jar my-application.jar The embedded server is created and configured automatically from your dependencies and Spring Boot configuration.
Spring Boot traditionally uses Tomcat as the default servlet container. If you prefer Jetty, you can exclude the Tomcat starter and include the Jetty starter instead.
The classic Maven configuration looks like this:
<!-- Exclude tomcat dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Include jetty dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency> In newer Spring Boot 4 projects you may also encounter spring-boot-starter-webmvc, which is the more explicit Spring MVC starter.
There is generally no reason to swap Tomcat simply because another server sounds faster. For most applications, application code, database access, external APIs, caching strategy and JVM behavior matter considerably more than the difference between mainstream servlet containers.
The following configuration was commonly used with earlier Spring Boot releases:
<!-- Exclude tomcat dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Include undertow dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency> Spring Boot 4 warning: Undertow is no longer one of Spring Boot 4’s supported embedded servlet containers. The example above remains relevant to applications running compatible Spring Boot 3.x releases, including Spring Boot 3.5. For a new Spring Boot 4 servlet application, use Tomcat or Jetty.
By default, a Spring Boot web application listens on port 8080.
You can change it in application.properties:
# HTTP Server port
server.port=8080 For example, to run the application on port 9090:
server.port=9090 The YAML equivalent is:
server:
port: 9090 You can also override a property at startup without modifying the packaged application:
java -jar myapp.jar --server.port=9090 Or provide it through the corresponding environment variable:
SERVER_PORT=9090 This is particularly useful in containers and deployment platforms where environment-specific configuration should not be baked into the JAR.
By default, the application is served from the root context /.
To expose it under a path such as:
http://localhost:8080/myapp configure:
# Make the application accessible on the given context path
server.servlet.context-path=/myapp Combined with the port configuration:
# HTTP Server port
server.port=8080
# Make the application accessible on the given context path (http://localhost:8080/myapp)
server.servlet.context-path=/myapp server.servlet.context-path applies to servlet applications. Spring WebFlux applications use their WebFlux-specific base-path configuration instead.
You can control the network address to which the embedded server binds using server.address.
server.address=127.0.0.1
server.port=8080 Binding to 127.0.0.1 can be useful when Nginx, Apache, HAProxy or another local reverse proxy is the only service that should communicate directly with Spring Boot.
Do not blindly change the address to 0.0.0.0 without understanding your deployment environment. It makes the application listen on all available interfaces, which may expose the service beyond what you intended.
HTTP response compression reduces the amount of data transferred between your application and its clients. It is particularly effective for text formats such as HTML, CSS, JavaScript, XML and JSON.
Response compression is disabled by default. Enable it with:
# Enable response compression
server.compression.enabled=true
# The comma-separated list of mime types that should be compressed
server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
# Compress the response only if the response size is at least 1KB
server.compression.min-response-size=1024 Modern Spring Boot already has a sensible default list of compressible MIME types, including HTML, XML, plain text, CSS, JavaScript and JSON. Therefore the minimal configuration may simply be:
server.compression.enabled=true The default minimum response size in current Spring Boot releases is 2 KB. You can override it explicitly:
server.compression.enabled=true
server.compression.min-response-size=2KB Compression has CPU overhead, which is why compressing tiny responses often provides little benefit. Images, ZIP archives, video and many other binary formats are already compressed and normally should not be passed through another compression layer.
If your application serves another text-based format, you can extend the configured MIME types. Newer Spring Boot versions also expose an additional MIME-types setting, allowing you to extend rather than redefine the defaults.
server.compression.enabled=true
server.compression.additional-mime-types=application/problem+json,application/ld+json Before tuning compression inside Spring Boot, also check whether your reverse proxy, CDN or ingress controller already performs compression. Duplicating responsibility across multiple layers makes troubleshooting harder.
HTTP/2 improves HTTP connection efficiency by allowing multiple request and response streams to share a connection. It also uses binary framing and header compression.
Enable Spring Boot HTTP/2 support with:
# Enable HTTP/2 support, if the current environment supports it
server.http2.enabled=true Spring Boot supports both HTTP/2 over TLS (h2) and clear-text HTTP/2 (h2c) where the chosen embedded server supports it.
For normal public websites, HTTP/2 is usually delivered over HTTPS. If TLS terminates at a reverse proxy or load balancer, the connection from that proxy to your Spring Boot application may instead use HTTP/1.1 or h2c depending on your infrastructure.
Do not confuse HTTP/2 with application speed. Enabling HTTP/2 can improve connection efficiency, but it will not fix slow database queries, inefficient APIs, thread starvation or poor application architecture.
If Spring Boot terminates HTTPS itself rather than sitting behind a TLS-terminating proxy, configure an SSL certificate for the embedded server.
A traditional PKCS12 configuration looks like:
server.port=8443
server.ssl.enabled=true
server.ssl.key-store=classpath:application.p12
server.ssl.key-store-password=${SSL_KEYSTORE_PASSWORD}
server.ssl.key-store-type=PKCS12 Keeping secrets such as the keystore password in an environment variable or external secret store is preferable to committing them to source control.
Modern Spring Boot also supports reusable SSL bundles. For example:
spring.ssl.bundle.jks.web.keystore.location=classpath:application.p12
spring.ssl.bundle.jks.web.keystore.password=${SSL_KEYSTORE_PASSWORD}
spring.ssl.bundle.jks.web.keystore.type=PKCS12
server.port=8443
server.ssl.bundle=web SSL bundles are useful when the same trust material needs to be configured consistently across servers and clients.
Spring Boot can also use PEM certificate and private-key files directly:
spring.ssl.bundle.pem.web.keystore.certificate=file:/etc/ssl/example/fullchain.pem
spring.ssl.bundle.pem.web.keystore.private-key=file:/etc/ssl/example/private-key.pem
server.ssl.bundle=web Current Spring Boot versions can reload compatible SSL bundles when certificate files change, which is useful when certificates are renewed externally.
HTTP compression saves bandwidth during a request. Browser caching can avoid the request entirely.
Cache-Control headers tell browsers and intermediary caches how a resource may be reused.
Modern Spring Boot uses the spring.web.resources namespace:
# Maximum time the response should be cached
spring.web.resources.cache.cachecontrol.max-age=120s
# Revalidate a stale resource before using it
spring.web.resources.cache.cachecontrol.must-revalidate=true For publicly cacheable static resources:
spring.web.resources.cache.cachecontrol.max-age=365d
spring.web.resources.cache.cachecontrol.cache-public=true A long cache lifetime such as one year should normally be used only when filenames are versioned or fingerprinted. Otherwise, a browser may continue serving an old CSS or JavaScript file after you deploy a new version.
Older versions of this tutorial used:
# Maximum time the response should be cached (in seconds)
spring.resources.cache.cachecontrol.max-age=120
# The cache must re-validate stale resources with the server. Any expired resources must not be used without re-validating.
spring.resources.cache.cachecontrol.must-revalidate=true And:
# The resources are private and intended for a single user. They must not be stored by a shared cache (e.g CDN).
spring.resources.cache.cachecontrol.cache-private= # set a boolean value true/false
# The resources are public and any cache may store the response.
spring.resources.cache.cachecontrol.cache-public= # set a boolean value true/false Do not copy these old
spring.resources.*properties into a modern Spring Boot application. Usespring.web.resources.*instead.
Spring Boot exposes considerably more than just max-age. Depending on your application, useful directives include:
cache-public — allows shared caches such as a CDN to store the response.cache-private — indicates that the response is intended for a private cache such as the user’s browser.no-cache — permits storage but requires validation before reuse.no-store — instructs caches not to store the response.must-revalidate — prevents stale content from being reused without validation.s-max-age — controls freshness specifically for shared caches.stale-while-revalidate — permits stale content to be served temporarily while it is refreshed.stale-if-error — allows stale content to be served temporarily when an upstream error occurs.For example:
spring.web.resources.cache.cachecontrol.max-age=1h
spring.web.resources.cache.cachecontrol.cache-public=true
spring.web.resources.cache.cachecontrol.stale-while-revalidate=60s Spring Boot automatically serves static content from conventional classpath locations such as /static, /public, /resources and /META-INF/resources.
You can customize resource locations when necessary:
spring.web.resources.static-locations=classpath:/static/,file:/opt/myapp/assets/ Be careful when serving arbitrary filesystem directories. Static resource configuration is part of your application’s security boundary.
Multipart handling allows controllers to receive uploaded files through multipart/form-data requests.
Multipart support is enabled with:
spring.servlet.multipart.enabled=true The original configuration example is:
# Write files to disk if the file size is more than 2KB.
spring.servlet.multipart.file-size-threshold=2KB
# The intermediate disk location where the uploaded files are written
spring.servlet.multipart.location=/tmp
# Maximum file size that can be uploaded
spring.servlet.multipart.max-file-size=50MB
# Maximum allowed multipart request size
spring.servlet.multipart.max-request-size=75MB max-file-size limits an individual uploaded file, while max-request-size limits the complete multipart request. A request containing several files can therefore exceed the request limit even if every individual file is below the per-file limit.
Do not set upload limits to extremely large values simply to make an upload error disappear. Upload limits help protect the application from accidental resource exhaustion and abusive requests.
If your Spring Boot application runs behind Nginx, Apache, a cloud load balancer, Kubernetes ingress or another proxy, that layer may impose its own request-body limit.
Increasing spring.servlet.multipart.max-file-size will not help if the proxy rejects the request before it reaches Spring Boot.
Spring Boot exposes a general limit for HTTP request headers:
server.max-http-request-header-size=16KB Current Spring Boot releases default to a smaller limit, so increase this only if your application has a legitimate reason, such as unusually large authentication or tracing headers.
Oversized headers are often a symptom rather than something that should automatically be accommodated. Large cookies in particular can create unnecessary bandwidth on every request.
Spring Boot exposes common server settings through the server.* namespace and container-specific settings through namespaces such as server.tomcat.*.
Examples include:
# Maximum form POST size
server.tomcat.max-http-form-post-size=2MB
# Maximum number of keep-alive requests
server.tomcat.max-keep-alive-requests=100
# Maximum number of request parameters parsed by Tomcat
server.tomcat.max-parameter-count=1000 There are also server-specific properties for threads, connections, access logging, response-header limits and other container behavior.
Avoid copying “high performance Tomcat settings” from random production configurations. Raising thread counts, connection limits and queues without load testing can increase memory consumption and latency rather than improve throughput.
Production Spring Boot applications commonly run behind a reverse proxy, ingress controller or cloud load balancer.
The proxy may receive a request such as:
https://example.com/account while forwarding it internally to something like:
http://10.0.1.42:8080/account Without correct forwarded-header handling, the application may believe the original request used HTTP rather than HTTPS or may generate URLs containing the internal host and port.
Spring Boot exposes:
server.forward-headers-strategy=native or, depending on your deployment:
server.forward-headers-strategy=framework The correct choice depends on your server and proxy setup.
Security warning: forwarded headers should only be trusted when your network architecture ensures that untrusted clients cannot spoof them directly. The proxy and application must agree on where the trust boundary is.
Graceful shutdown allows an application instance to finish existing requests while it is being stopped instead of terminating active traffic immediately.
This is especially important with rolling deployments, container orchestration and load-balanced applications.
In current Spring Boot releases, graceful shutdown is enabled by default for the supported embedded servers.
You can configure how long a shutdown phase may take:
spring.lifecycle.timeout-per-shutdown-phase=20s If you specifically need immediate shutdown instead:
server.shutdown=immediate For most production HTTP services, graceful shutdown is the safer default.
Servlet-based applications that use HTTP sessions can configure session lifetime and cookie behavior through server.servlet.session.*.
server.servlet.session.timeout=30m
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true
server.servlet.session.cookie.same-site=lax HttpOnly prevents normal browser JavaScript from reading the cookie. Secure restricts the cookie to HTTPS connections. SameSite influences when cookies are sent in cross-site requests.
The correct SameSite value depends on how authentication, embedded content and cross-origin integrations work in your application, so do not change it without testing those flows.
Spring Boot exposes the server response header through:
server.server-header=MyApplication In many deployments there is little value in advertising implementation details through HTTP headers. The exact headers ultimately seen by clients may also be modified by a reverse proxy or CDN.
Spring Boot supports both application.properties and YAML configuration.
For example:
server.port=8080
server.compression.enabled=true
server.http2.enabled=true
spring.lifecycle.timeout-per-shutdown-phase=20s is equivalent to:
server:
port: 8080
compression:
enabled: true
http2:
enabled: true
spring:
lifecycle:
timeout-per-shutdown-phase: 20s Neither format is inherently better. Properties files are compact and easy to search; YAML can be easier to read when configuration is deeply nested.
Development and production environments rarely need identical server settings.
You can use Spring profiles, for example:
application.properties
application-dev.properties
application-prod.properties Then activate a profile:
spring.profiles.active=prod or externally:
SPRING_PROFILES_ACTIVE=prod java -jar myapp.jar Production secrets should normally be supplied by environment variables, mounted configuration, a secrets manager or another deployment-specific mechanism instead of being committed to application-prod.properties.
There is no universal “best” Spring Boot configuration, but the following example illustrates how several of the settings discussed above fit together:
# Server
server.port=8080
server.servlet.context-path=/
# Response compression
server.compression.enabled=true
server.compression.min-response-size=2KB
# HTTP/2
server.http2.enabled=true
# Reverse proxy handling
server.forward-headers-strategy=native
# Request protection
server.max-http-request-header-size=16KB
# Static resources
spring.web.resources.cache.cachecontrol.max-age=1h
spring.web.resources.cache.cachecontrol.cache-public=true
# Multipart uploads
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-request-size=25MB
# Graceful shutdown
spring.lifecycle.timeout-per-shutdown-phase=20s
# Session security - relevant only when using servlet HTTP sessions
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true
server.servlet.session.cookie.same-site=lax Treat this as an example, not a configuration file to copy blindly. Whether a setting is appropriate depends on whether TLS terminates in Spring Boot or at a proxy, whether static assets are served by the application or a CDN, whether the application accepts uploads, whether it uses HTTP sessions and how it is deployed.
Spring Boot supports externalized configuration, which means a packaged application’s defaults can be overridden without rebuilding it.
In practice, you may encounter configuration coming from:
application.properties or application.ymlThis is useful, but it can also explain mysterious production behavior. If server.port=8080 appears in your repository but the application starts on another port, check whether the property is being overridden externally.
Spring Boot’s configuration model evolves. A tutorial written for Spring Boot 1.x or 2.x may contain property names that have since moved or disappeared.
The static-resource change from spring.resources.* to spring.web.resources.* is a good example.
Changing thread pools, queues and connection limits without measurements is not performance engineering. Start with metrics and load testing, find the bottleneck and tune the component responsible for it.
The effective behavior users experience may be determined by several layers:
Browser
↓
CDN
↓
Load balancer / reverse proxy
↓
Spring Boot embedded server
↓
Application Compression, TLS, maximum body size, caching, HTTP/2 and timeouts may exist at more than one of these layers.
Request limits exist for a reason. Increase them to a documented business requirement, not to effectively unlimited values.
Properties files commonly end up in Git repositories, build artifacts and backups. Use external secrets for database passwords, API credentials and TLS keystore passwords.
If app.js is cached for a year and you deploy a different app.js tomorrow, returning visitors can continue running the old version. Long-lived caching works best with fingerprinted filenames such as app.a81f94c.js.
The default HTTP port is 8080. Change it with server.port.
Spring MVC applications normally use embedded Tomcat by default. Jetty can be substituted. Spring WebFlux applications commonly use Reactor Netty.
Not as one of Spring Boot 4’s supported embedded servlet servers. Undertow remains relevant to compatible Spring Boot 3.x applications, but new Spring Boot 4 servlet projects should use Tomcat or Jetty.
No. Response compression is disabled by default. Enable it with server.compression.enabled=true.
Current Spring Boot releases use a default minimum response size of 2 KB. Configure it with server.compression.min-response-size.
Set server.http2.enabled=true. The environment and embedded web server must also support the required HTTP/2 mode.
For static resources in modern Spring Boot, configure properties under spring.web.resources.cache.cachecontrol. For example, spring.web.resources.cache.cachecontrol.max-age=1h.
Use spring.servlet.multipart.max-file-size for an individual file and spring.servlet.multipart.max-request-size for the entire multipart request.
Yes, in current Spring Boot releases graceful shutdown is enabled by default for the supported embedded servers. Configure the allowed shutdown phase duration with spring.lifecycle.timeout-per-shutdown-phase.
It can, and that is perfectly reasonable for many applications. For high-traffic public assets, however, a CDN or reverse proxy can often cache and deliver static resources more efficiently while reducing load on the application.
# Port
server.port=8080
# Bind address
server.address=127.0.0.1
# Servlet context path
server.servlet.context-path=/myapp
# Compression
server.compression.enabled=true
server.compression.min-response-size=2KB
# HTTP/2
server.http2.enabled=true
# Forwarded headers
server.forward-headers-strategy=native
# Maximum HTTP request header size
server.max-http-request-header-size=16KB
# Static resource caching
spring.web.resources.cache.cachecontrol.max-age=1h
spring.web.resources.cache.cachecontrol.cache-public=true
# Multipart uploads
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=50MB
spring.servlet.multipart.max-request-size=75MB
# Graceful shutdown timeout
spring.lifecycle.timeout-per-shutdown-phase=20s
# Session
server.servlet.session.timeout=30m
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true
server.servlet.session.cookie.same-site=lax Spring Boot’s defaults are intentionally good enough to start an application without spending hours configuring its web server. Production deployments still benefit from explicitly reviewing the settings that affect networking, security, performance and resource usage.
The most important settings to understand are usually server.port, server.servlet.context-path, response compression, HTTP/2, TLS, static-resource caching, multipart limits, forwarded headers and graceful shutdown.
Before adding a configuration property from an old tutorial, verify it against the documentation for the Spring Boot version you actually run. Server integrations and property namespaces do change between major releases.
The authoritative reference is the Spring Boot Common Application Properties documentation, which lists the configuration properties exposed by the current release.
A practical Triumphoid guide to claude vs chatgpt for long wordpress drafts: my honest workflow…
Primary architectural pillar covering system failure mitigations, state management, synchronous database backpressures, and decoupling methods…
A practical Triumphoid guide to rankmath vs yoast in an ai publishing workflow: why i…
A practical Triumphoid guide to my weekly wordpress ai publishing review routine, with first-person workflow…
Core engineering perspective analyzing structural context drifting, agent token waste cycles, and deterministic governance limits…
A practical Triumphoid guide to how i decide which wordpress tasks should not be automated,…