From 218fe0a663982eaca2837e05ea5e439f70f88078 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sun, 22 Feb 2026 20:16:17 -0800 Subject: [PATCH] codegen: auto-register HEAD guard for #[get] routes Per RFC 7231 Section 4.3.2, a server that supports GET must also support HEAD. The #[get] macro now implicitly registers a HEAD guard so that HEAD requests are handled without requiring users to write #[route("/path", method="GET", method="HEAD")]. Fixes #2702 --- actix-web-codegen/src/route.rs | 6 ++++++ actix-web-codegen/tests/routes.rs | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index cd1ad4c66..955522f61 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -240,6 +240,12 @@ impl Args { let is_route_macro = method.is_none(); if let Some(method) = method { + // Per RFC 7231 Section 4.3.2, a server that supports GET must also + // support HEAD. Automatically register a HEAD guard alongside GET + // so that `#[get(...)]` handlers respond to HEAD requests. + if method == MethodType::Get { + methods.insert(MethodTypeExt::Standard(MethodType::Head)); + } methods.insert(MethodTypeExt::Standard(method)); } diff --git a/actix-web-codegen/tests/routes.rs b/actix-web-codegen/tests/routes.rs index 1443f9a75..2dbf70c9a 100644 --- a/actix-web-codegen/tests/routes.rs +++ b/actix-web-codegen/tests/routes.rs @@ -384,3 +384,23 @@ async fn test_wrap() { let body = String::from_utf8(body.to_vec()).unwrap(); assert!(body.contains("wrong number of parameters")); } + +#[get("/head-from-get")] +async fn get_head_implicit() -> impl Responder { + HttpResponse::Ok().body("hello") +} + +#[actix_web::test] +async fn test_get_implies_head() { + let srv = actix_test::start(|| App::new().service(get_head_implicit)); + + // GET should work as usual + let request = srv.request(http::Method::GET, srv.url("/head-from-get")); + let response = request.send().await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); + + // HEAD should also work on a #[get] endpoint per RFC 7231 + let request = srv.request(http::Method::HEAD, srv.url("/head-from-get")); + let response = request.send().await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); +}