diff --git a/aws_lambda_powertools/event_handler/api_gateway.py b/aws_lambda_powertools/event_handler/api_gateway.py
index 7c4d676931e..0edfe985cd7 100644
--- a/aws_lambda_powertools/event_handler/api_gateway.py
+++ b/aws_lambda_powertools/event_handler/api_gateway.py
@@ -30,6 +30,8 @@
     cast,
 )
 
+from typing_extensions import override
+
 from aws_lambda_powertools.event_handler import content_types
 from aws_lambda_powertools.event_handler.exceptions import NotFoundError, ServiceError
 from aws_lambda_powertools.event_handler.openapi.constants import DEFAULT_API_VERSION, DEFAULT_OPENAPI_VERSION
@@ -2652,3 +2654,24 @@ def __init__(
     def _get_base_path(self) -> str:
         # ALB doesn't have a stage variable, so we just return an empty string
         return ""
+
+    @override
+    def _to_response(self, result: Union[Dict, Tuple, Response]) -> Response:
+        """Convert the route's result to a Response
+
+        ALB requires a non-null body otherwise it converts as HTTP 5xx
+
+         3 main result types are supported:
+
+        - Dict[str, Any]: Rest api response with just the Dict to json stringify and content-type is set to
+          application/json
+        - Tuple[dict, int]: Same dict handling as above but with the option of including a status code
+        - Response: returned as is, and allows for more flexibility
+        """
+
+        # NOTE: Minor override for early return on Response with null body for ALB
+        if isinstance(result, Response) and result.body is None:
+            logger.debug("ALB doesn't allow None responses; converting to empty string")
+            result.body = ""
+
+        return super()._to_response(result)
diff --git a/docs/core/event_handler/api_gateway.md b/docs/core/event_handler/api_gateway.md
index a8e0ac6a051..b3c046e243c 100644
--- a/docs/core/event_handler/api_gateway.md
+++ b/docs/core/event_handler/api_gateway.md
@@ -51,15 +51,45 @@ This is the sample infrastructure for API Gateway and Lambda Function URLs we ar
 
 ### Event Resolvers
 
-Before you decorate your functions to handle a given path and HTTP method(s), you need to initialize a resolver.
+Before you decorate your functions to handle a given path and HTTP method(s), you need to initialize a resolver. A resolver will handle request resolution, including [one or more routers](#split-routes-with-router), and give you access to the current event via typed properties.
 
-A resolver will handle request resolution, including [one or more routers](#split-routes-with-router), and give you access to the current event via typed properties.
+By default, we will use `APIGatewayRestResolver` throughout the documentation. You can use any of the following:
 
-For resolvers, we provide: `APIGatewayRestResolver`, `APIGatewayHttpResolver`, `ALBResolver`, `LambdaFunctionUrlResolver`, and `VPCLatticeResolver`. From here on, we will default to `APIGatewayRestResolver` across examples.
+| Resolver                                                | AWS service                            |
+| ------------------------------------------------------- | -------------------------------------- |
+| **[`APIGatewayRestResolver`](#api-gateway-rest-api)**   | Amazon API Gateway REST API            |
+| **[`APIGatewayHttpResolver`](#api-gateway-http-api)**   | Amazon API Gateway HTTP API            |
+| **[`ALBResolver`](#application-load-balancer)**         | Amazon Application Load Balancer (ALB) |
+| **[`LambdaFunctionUrlResolver`](#lambda-function-url)** | AWS Lambda Function URL                |
+| **[`VPCLatticeResolver`](#vpc-lattice)**                | Amazon VPC Lattice                     |
 
-???+ info "Auto-serialization"
-    We serialize `Dict` responses as JSON, trim whitespace for compact responses, set content-type to `application/json`, and
-    return a 200 OK HTTP status. You can optionally set a different HTTP status code as the second argument of the tuple:
+#### Response auto-serialization
+
+> Want full control of the response, headers and status code? [Read about `Response` object here](#fine-grained-responses).
+
+For your convenience, we automatically perform these if you return a dictionary response:
+
+1. Auto-serialize `dictionary` responses to JSON and trim it
+2. Include the response under each resolver's equivalent of a `body`
+3. Set `Content-Type` to `application/json`
+4. Set `status_code` to 200 (OK)
+
+=== "getting_started_resolvers_response_serialization.py"
+
+    ```python hl_lines="9"
+    --8<-- "examples/event_handler_rest/src/getting_started_resolvers_response_serialization.py"
+    ```
+
+    1. This dictionary will be serialized, trimmed, and included under the `body` key
+
+=== "getting_started_resolvers_response_serialization_output.json"
+
+    ```json hl_lines="8"
+    --8<-- "examples/event_handler_rest/src/getting_started_resolvers_response_serialization_output.json"
+    ```
+
+??? info "Coming from Flask? We also support tuple response"
+    You can optionally set a different HTTP status code as the second argument of the tuple.
 
     ```python hl_lines="15 16"
     --8<-- "examples/event_handler_rest/src/getting_started_return_tuple.py"
@@ -462,16 +492,16 @@ In the following example, we use a new `Header` OpenAPI type to add [one out of
 
 With data validation enabled, we natively support serializing the following data types to JSON:
 
-| Data type                                                            | Serialized type                                                                  |
-| -------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
-| **Pydantic models**                                                  | `dict`                                                                           |
-| **Python Dataclasses**                                               | `dict`                                                                           |
-| **Enum**                                                             | Enum values                                                                      |
-| **Datetime**                                                         | Datetime ISO format string                                                       |
-| **Decimal**                                                          | `int` if no exponent, or `float`                                                 |
-| **Path**                                                             | `str`                                                                            |
-| **UUID**                                                             | `str`                                                                            |
-| **Set**                                                              | `list`                                                                           |
+| Data type                                                            | Serialized type                                                                                                                               |
+| -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Pydantic models**                                                  | `dict`                                                                                                                                        |
+| **Python Dataclasses**                                               | `dict`                                                                                                                                        |
+| **Enum**                                                             | Enum values                                                                                                                                   |
+| **Datetime**                                                         | Datetime ISO format string                                                                                                                    |
+| **Decimal**                                                          | `int` if no exponent, or `float`                                                                                                              |
+| **Path**                                                             | `str`                                                                                                                                         |
+| **UUID**                                                             | `str`                                                                                                                                         |
+| **Set**                                                              | `list`                                                                                                                                        |
 | **Python primitives** _(dict, string, sequences, numbers, booleans)_ | [Python's default JSON serializable types](https://docs.python.org/3/library/json.html#encoders-and-decoders){target="_blank" rel="nofollow"} |
 
 ???+ info "See [custom serializer section](#custom-serializer) for bringing your own."
diff --git a/examples/event_handler_rest/src/getting_started_resolvers_response_serialization.py b/examples/event_handler_rest/src/getting_started_resolvers_response_serialization.py
new file mode 100644
index 00000000000..756b3a0aa33
--- /dev/null
+++ b/examples/event_handler_rest/src/getting_started_resolvers_response_serialization.py
@@ -0,0 +1,13 @@
+from aws_lambda_powertools.event_handler import APIGatewayRestResolver
+from aws_lambda_powertools.utilities.typing.lambda_context import LambdaContext
+
+app = APIGatewayRestResolver()
+
+
+@app.get("/ping")
+def ping():
+    return {"message": "pong"}  # (1)!
+
+
+def lambda_handler(event: dict, context: LambdaContext) -> dict:
+    return app.resolve(event, context)
diff --git a/examples/event_handler_rest/src/getting_started_resolvers_response_serialization_output.json b/examples/event_handler_rest/src/getting_started_resolvers_response_serialization_output.json
new file mode 100644
index 00000000000..16876281b41
--- /dev/null
+++ b/examples/event_handler_rest/src/getting_started_resolvers_response_serialization_output.json
@@ -0,0 +1,10 @@
+{
+    "statusCode": 200,
+    "multiValueHeaders": {
+        "Content-Type": [
+            "application/json"
+        ]
+    },
+    "body": "{'message':'pong'}",
+    "isBase64Encoded": false
+}
\ No newline at end of file
diff --git a/tests/e2e/event_handler/handlers/alb_handler_with_body_none.py b/tests/e2e/event_handler/handlers/alb_handler_with_body_none.py
new file mode 100644
index 00000000000..ec72bfbd5f7
--- /dev/null
+++ b/tests/e2e/event_handler/handlers/alb_handler_with_body_none.py
@@ -0,0 +1,17 @@
+from aws_lambda_powertools.event_handler import (
+    ALBResolver,
+    Response,
+)
+
+app = ALBResolver()
+
+
+@app.get("/todos_with_no_body")
+def todos():
+    return Response(
+        status_code=200,
+    )
+
+
+def lambda_handler(event, context):
+    return app.resolve(event, context)
diff --git a/tests/e2e/event_handler/infrastructure.py b/tests/e2e/event_handler/infrastructure.py
index 8d7f98045e1..5fd78896a34 100644
--- a/tests/e2e/event_handler/infrastructure.py
+++ b/tests/e2e/event_handler/infrastructure.py
@@ -1,4 +1,4 @@
-from typing import Dict, Optional
+from typing import Dict, List, Optional
 
 from aws_cdk import CfnOutput
 from aws_cdk import aws_apigateway as apigwv1
@@ -17,12 +17,12 @@ class EventHandlerStack(BaseInfrastructure):
     def create_resources(self):
         functions = self.create_lambda_functions()
 
-        self._create_alb(function=functions["AlbHandler"])
+        self._create_alb(function=[functions["AlbHandler"], functions["AlbHandlerWithBodyNone"]])
         self._create_api_gateway_rest(function=functions["ApiGatewayRestHandler"])
         self._create_api_gateway_http(function=functions["ApiGatewayHttpHandler"])
         self._create_lambda_function_url(function=functions["LambdaFunctionUrlHandler"])
 
-    def _create_alb(self, function: Function):
+    def _create_alb(self, function: List[Function]):
         vpc = ec2.Vpc.from_lookup(
             self.stack,
             "VPC",
@@ -33,15 +33,19 @@ def _create_alb(self, function: Function):
         alb = elbv2.ApplicationLoadBalancer(self.stack, "ALB", vpc=vpc, internet_facing=True)
         CfnOutput(self.stack, "ALBDnsName", value=alb.load_balancer_dns_name)
 
-        self._create_alb_listener(alb=alb, name="Basic", port=80, function=function)
+        # Function with Body
+        self._create_alb_listener(alb=alb, name="Basic", port=80, function=function[0])
         self._create_alb_listener(
             alb=alb,
             name="MultiValueHeader",
             port=8080,
-            function=function,
+            function=function[0],
             attributes={"lambda.multi_value_headers.enabled": "true"},
         )
 
+        # Function without Body
+        self._create_alb_listener(alb=alb, name="BasicWithoutBody", port=8081, function=function[1])
+
     def _create_alb_listener(
         self,
         alb: elbv2.ApplicationLoadBalancer,
diff --git a/tests/e2e/event_handler/test_response_code.py b/tests/e2e/event_handler/test_response_code.py
new file mode 100644
index 00000000000..46bf8bcf183
--- /dev/null
+++ b/tests/e2e/event_handler/test_response_code.py
@@ -0,0 +1,29 @@
+import pytest
+from requests import Request
+
+from tests.e2e.utils import data_fetcher
+from tests.e2e.utils.auth import build_iam_auth
+
+
+@pytest.fixture
+def alb_basic_without_body_listener_endpoint(infrastructure: dict) -> str:
+    dns_name = infrastructure.get("ALBDnsName")
+    port = infrastructure.get("ALBBasicWithoutBodyListenerPort", "")
+    return f"http://{dns_name}:{port}"
+
+
+@pytest.mark.xdist_group(name="event_handler")
+def test_alb_with_body_empty(alb_basic_without_body_listener_endpoint):
+    # GIVEN url has a trailing slash - it should behave as if there was not one
+    url = f"{alb_basic_without_body_listener_endpoint}/todos_with_no_body"
+
+    # WHEN calling an invalid URL (with trailing slash) expect HTTPError exception from data_fetcher
+    response = data_fetcher.get_http_response(
+        Request(
+            method="GET",
+            url=url,
+            auth=build_iam_auth(url=url, aws_service="lambda"),
+        ),
+    )
+
+    assert response.status_code == 200
diff --git a/tests/functional/event_handler/required_dependencies/test_api_gateway.py b/tests/functional/event_handler/required_dependencies/test_api_gateway.py
index efd7edf0e5e..ecd514aa0ee 100644
--- a/tests/functional/event_handler/required_dependencies/test_api_gateway.py
+++ b/tests/functional/event_handler/required_dependencies/test_api_gateway.py
@@ -1820,3 +1820,21 @@ def static_handler() -> Response:
     # THEN the static_handler should have been called, because it fully matches the path directly
     response_body = json.loads(response["body"])
     assert response_body["hello"] == "static"
+
+
+def test_alb_empty_response_object():
+    # GIVEN an ALB Resolver
+    app = ALBResolver()
+    event = {"path": "/my/request", "httpMethod": "GET"}
+
+    # AND route returns a Response object with empty body
+    @app.get("/my/request")
+    def opa():
+        return Response(status_code=200, content_type=content_types.APPLICATION_JSON)
+
+    # WHEN calling the event handler
+    result = app(event, {})
+
+    # THEN body should be converted to an empty string
+    assert result["statusCode"] == 200
+    assert result["body"] == ""