diff --git a/fastapi/routing.py b/fastapi/routing.py index 4793f1305c..39592d9436 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -1011,10 +1011,8 @@ class APIRoute(routing.Route): # RFC 7231 §4.3.2: the server SHOULD send the same header fields in # response to a HEAD request as it would have sent for GET. # HEAD is NOT added to self.methods so it stays out of the OpenAPI - # schema. Instead, we promote the match at routing time and allow - # it through in handle(). - if match == Match.PARTIAL and self._is_head_for_get(scope): - match = Match.FULL + # schema. Keep the match partial so an explicit HEAD route registered + # later can still take precedence. if match != Match.NONE: child_scope["route"] = self return match, child_scope diff --git a/tests/test_extra_routes.py b/tests/test_extra_routes.py index a4ca77473d..985adb9439 100644 --- a/tests/test_extra_routes.py +++ b/tests/test_extra_routes.py @@ -71,13 +71,9 @@ def test_delete(): def test_head(): - # HEAD is served by the GET handler (registered first) because GET - # routes automatically support HEAD per RFC 7231 §4.3.2. The explicit - # @app.head handler is shadowed since the GET route matches first. response = client.head("/items/foo") assert response.status_code == 200, response.text - assert response.content == b"" - assert response.headers["content-type"] == "application/json" + assert response.headers["x-fastapi-item-id"] == "foo" def test_options():