Close

Spring MVC - Built-in support for HTTP HEAD and HTTP OPTIONS

[Last Updated: Mar 20, 2026]

In this tutorial, we will explore Spring MVC's built-in support for HTTP HEAD and OPTIONS methods. You'll learn how Spring automatically handles HEAD requests through your existing GET mappings, and how it generates OPTIONS responses with the appropriate Allow headers—all without writing any additional controller code.

These features are only available starting Spring 4.3.

Implicit HEAD support

Any @RequestMapping method that handles GET requests automatically supports HEAD as well—you don't need to declare HEAD separately. When a HEAD request arrives, Spring executes the same handler method but discards the response body. Instead of sending the body, it calculates its size and sets the Content-Length header with that value.


That means we never have to separately create a handler method for HTTP HEAD verb as spring implicitly supports that, given that GET verb is already defined for the target URL.

Implicit OPTIONS support

Spring MVC also provides built-in support for HTTP OPTIONS. When an OPTIONS request arrives, Spring automatically sets the Allow header in the response to list all HTTP methods supported by that URL—based on the @RequestMapping annotations you've declared. If a URL has no explicitly declared methods, Spring defaults the Allow header to: GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS

That means, we never have to separately create a handler method for HTTP OPTIONS verb as spring implicitly supports that, given that all handler methods explicitly specify the HTTP method with each @RequestMapping for the target URL.



Example

To try examples, run embedded Jetty (configured in pom.xml of example project below):

mvn jetty:run

The Controller

Let's create a very simple controller with a handler method populating some headers:

package com.logicbig.example;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.Arrays;
import java.util.logging.Logger;

@Controller
public class MyController {
    Logger logger = Logger.getLogger(MyController.class.getSimpleName());

    @RequestMapping(value = "test", method = {RequestMethod.GET})
    public HttpEntity<String> handleTestRequest () {

        HttpEntity<String> responseEntity = new HttpEntity<>("test body");
        logger.info("handler finished");
        return responseEntity;
    }
}

GET request

$ curl -s http://localhost:8080/test
test body

HEAD request

To make a HEAD request with curl, use the -I (uppercase "i") or --head flag.

$ curl -s --head http://localhost:8080/test
HTTP/1.1 200 OK
Server: Jetty(12.1.6)
Date: Fri, 20 Mar 2026 10:16:24 GMT
Content-Type: text/plain;charset=iso-8859-1
Content-Length: 9

Note that this time no body content printed, just headers.


OPTIONS Request

Let's send curl request with OPTIONS:

$ curl -s -X OPTIONS http://localhost:8080/test -i
HTTP/1.1 200 OK
Server: Jetty(12.1.6)
Date: Fri, 20 Mar 2026 10:16:35 GMT
Allow: GET,HEAD,OPTIONS
Accept-Patch:
Content-Length: 0

The above response has 'Allow' header. There's an extra HEAD option because of Spring implicit support of HEAD for each GET method.

Also in this case no handler methods are called. Spring returns all available methods associated with the url in the Allow header.

Unit Tests

package com.logicbig.example;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = MyWebConfig.class)
public class ControllerTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvcTester mockMvc;

    @BeforeEach
    public void setup() {
        // Pure Spring MVC setup for MockMvcTester
        this.mockMvc = MockMvcTester.from(this.wac);
    }

    @Test
    public void testGet() {
        assertThat(mockMvc.get().uri("/test"))
                .hasStatusOk()
                .bodyText().isEqualTo("test body");
    }

    @Test
    public void testHead() {
        assertThat(mockMvc.head().uri("/test"))
                .hasStatusOk();

    }

    @Test
    public void testOptions() {
        assertThat(mockMvc.options().uri("/test"))
                .hasStatusOk()
                .headers()
                .hasValue("Allow", "GET,HEAD,OPTIONS");
    }
}
mvn clean test -Dtest=ControllerTest

Output

$ mvn clean test -Dtest=ControllerTest
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for com.logicbig.com:spring-mvc-head-options-method-example:war:1.0-SNAPSHOT
[WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-war-plugin is missing. @ line 47, column 21
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]
[INFO]
[INFO] ------< com.logicbig.com:spring-mvc-head-options-method-example >-------
[INFO] Building spring-mvc-head-options-method-example 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ spring-mvc-head-options-method-example ---
[INFO] Deleting D:\example-projects\spring-mvc\spring-mvc-head-options-method-example\target
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ spring-mvc-head-options-method-example ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\spring-mvc-head-options-method-example\src\main\resources
[INFO]
[INFO] --- compiler:3.3:compile (default-compile) @ spring-mvc-head-options-method-example ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 3 source files to D:\example-projects\spring-mvc\spring-mvc-head-options-method-example\target\classes
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ spring-mvc-head-options-method-example ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\spring-mvc-head-options-method-example\src\test\resources
[INFO]
[INFO] --- compiler:3.3:testCompile (default-testCompile) @ spring-mvc-head-options-method-example ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to D:\example-projects\spring-mvc\spring-mvc-head-options-method-example\target\test-classes
[INFO]
[INFO] --- surefire:3.2.5:test (default-test) @ spring-mvc-head-options-method-example ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[WARNING] file.encoding cannot be set as system property, use <argLine>-Dfile.encoding=...</argLine> instead
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.logicbig.example.ControllerTest
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.248 s -- in com.logicbig.example.ControllerTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.615 s
[INFO] Finished at: 2026-03-20T18:09:08+08:00
[INFO] ------------------------------------------------------------------------
INFO: Completed initialization in 2 ms
INFO: Completed initialization in 0 ms
Mar 20, 2026 6:09:08 PM com.logicbig.example.MyController handleTestRequest
INFO: handler finished
INFO: Completed initialization in 1 ms
Mar 20, 2026 6:09:08 PM com.logicbig.example.MyController handleTestRequest
INFO: handler finished

Example Project

Dependencies and Technologies Used:

  • spring-webmvc 7.0.6 (Spring Web MVC)
     Version Compatibility: 4.3.0.RELEASE - 7.0.6Version List
    ×

    Version compatibilities of spring-webmvc with this example:

      javax.servlet-api:3.x
    • 4.3.0.RELEASE
    • 4.3.1.RELEASE
    • 4.3.2.RELEASE
    • 4.3.3.RELEASE
    • 4.3.4.RELEASE
    • 4.3.5.RELEASE
    • 4.3.6.RELEASE
    • 4.3.7.RELEASE
    • 4.3.8.RELEASE
    • 4.3.9.RELEASE
    • 4.3.10.RELEASE
    • 4.3.11.RELEASE
    • 4.3.12.RELEASE
    • 4.3.13.RELEASE
    • 4.3.14.RELEASE
    • 4.3.15.RELEASE
    • 4.3.16.RELEASE
    • 4.3.17.RELEASE
    • 4.3.18.RELEASE
    • 4.3.19.RELEASE
    • 4.3.20.RELEASE
    • 4.3.21.RELEASE
    • 4.3.22.RELEASE
    • 4.3.23.RELEASE
    • 4.3.24.RELEASE
    • 4.3.25.RELEASE
    • 4.3.26.RELEASE
    • 4.3.27.RELEASE
    • 4.3.28.RELEASE
    • 4.3.29.RELEASE
    • 4.3.30.RELEASE
    • 5.0.0.RELEASE
    • 5.0.1.RELEASE
    • 5.0.2.RELEASE
    • 5.0.3.RELEASE
    • 5.0.4.RELEASE
    • 5.0.5.RELEASE
    • 5.0.6.RELEASE
    • 5.0.7.RELEASE
    • 5.0.8.RELEASE
    • 5.0.9.RELEASE
    • 5.0.10.RELEASE
    • 5.0.11.RELEASE
    • 5.0.12.RELEASE
    • 5.0.13.RELEASE
    • 5.0.14.RELEASE
    • 5.0.15.RELEASE
    • 5.0.16.RELEASE
    • 5.0.17.RELEASE
    • 5.0.18.RELEASE
    • 5.0.19.RELEASE
    • 5.0.20.RELEASE
    • 5.1.0.RELEASE
    • 5.1.1.RELEASE
    • 5.1.2.RELEASE
    • 5.1.3.RELEASE
    • 5.1.4.RELEASE
    • 5.1.5.RELEASE
    • 5.1.6.RELEASE
    • 5.1.7.RELEASE
    • 5.1.8.RELEASE
    • 5.1.9.RELEASE
    • 5.1.10.RELEASE
    • 5.1.11.RELEASE
    • 5.1.12.RELEASE
    • 5.1.13.RELEASE
    • 5.1.14.RELEASE
    • 5.1.15.RELEASE
    • 5.1.16.RELEASE
    • 5.1.17.RELEASE
    • 5.1.18.RELEASE
    • 5.1.19.RELEASE
    • 5.1.20.RELEASE
    • 5.2.0.RELEASE
    • 5.2.1.RELEASE
    • 5.2.2.RELEASE
    • 5.2.3.RELEASE
    • 5.2.4.RELEASE
    • 5.2.5.RELEASE
    • 5.2.6.RELEASE
    • 5.2.7.RELEASE
    • 5.2.8.RELEASE
    • 5.2.9.RELEASE
    • 5.2.10.RELEASE
    • 5.2.11.RELEASE
    • 5.2.12.RELEASE
    • 5.2.13.RELEASE
    • 5.2.14.RELEASE
    • 5.2.15.RELEASE
    • 5.2.16.RELEASE
    • 5.2.17.RELEASE
    • 5.2.18.RELEASE
    • 5.2.19.RELEASE
    • 5.2.20.RELEASE
    • 5.2.21.RELEASE
    • 5.2.22.RELEASE
    • 5.2.23.RELEASE
    • 5.2.24.RELEASE
    • 5.2.25.RELEASE
    • 5.3.0
    • 5.3.1
    • 5.3.2
    • 5.3.3
    • 5.3.4
    • javax.servlet-api:4.x
    • 5.3.5
    • 5.3.6
    • 5.3.7
    • 5.3.8
    • 5.3.9
    • 5.3.10
    • 5.3.11
    • 5.3.12
    • 5.3.13
    • 5.3.14
    • 5.3.15
    • 5.3.16
    • 5.3.17
    • 5.3.18
    • 5.3.19
    • 5.3.20
    • 5.3.21
    • 5.3.22
    • 5.3.23
    • 5.3.24
    • 5.3.25
    • 5.3.26
    • 5.3.27
    • 5.3.28
    • 5.3.29
    • 5.3.30
    • 5.3.31
    • 5.3.32
    • 5.3.33
    • 5.3.34
    • 5.3.35
    • 5.3.36
    • 5.3.37
    • 5.3.38
    • 5.3.39
    • javax.* -> jakarta.*
      jakarta.servlet-api:6.x
      Java 17 min
    • 6.0.0
    • 6.0.1
    • 6.0.2
    • 6.0.3
    • 6.0.4
    • 6.0.5
    • 6.0.6
    • 6.0.7
    • 6.0.8
    • 6.0.9
    • 6.0.10
    • 6.0.11
    • 6.0.12
    • 6.0.13
    • 6.0.14
    • 6.0.15
    • 6.0.16
    • 6.0.17
    • 6.0.18
    • 6.0.19
    • 6.0.20
    • 6.0.21
    • 6.0.22
    • 6.0.23
    • 6.1.0
    • 6.1.1
    • 6.1.2
    • 6.1.3
    • 6.1.4
    • 6.1.5
    • 6.1.6
    • 6.1.7
    • 6.1.8
    • 6.1.9
    • 6.1.10
    • 6.1.11
    • 6.1.12
    • 6.1.13
    • 6.1.14
    • 6.1.15
    • 6.1.16
    • 6.1.17
    • 6.1.18
    • 6.1.19
    • 6.1.20
    • 6.1.21
    • 6.2.0
    • 6.2.1
    • 6.2.2
    • 6.2.3
    • 6.2.4
    • 6.2.5
    • 6.2.6
    • 6.2.7
    • 6.2.8
    • 6.2.9
    • 6.2.10
    • 6.2.11
    • 6.2.12
    • 6.2.13
    • 6.2.14
    • 6.2.15
    • 6.2.16
    • 6.2.17
    • 7.0.0
    • 7.0.1
    • 7.0.2
    • 7.0.3
    • 7.0.4
    • 7.0.5
    • 7.0.6

    Versions in green have been tested.

  • spring-test 7.0.6 (Spring TestContext Framework)
  • jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
  • hamcrest 3.0 (Core API and libraries of hamcrest matcher framework)
  • assertj-core 3.26.3 (Rich and fluent assertions for testing in Java)
  • junit-jupiter-engine 6.0.3 (Module "junit-jupiter-engine" of JUnit)
  • JDK 25
  • Maven 3.9.11

Spring MVC - HTTP HEAD and HTTP OPTIONS Select All Download
  • spring-mvc-head-options-method-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • MyController.java
        • test
          • java
            • com
              • logicbig
                • example

    See Also

    Join