diff --git a/sanic/server/websockets/connection.py b/sanic/server/websockets/connection.py
index 87881b84..8ff19da6 100644
--- a/sanic/server/websockets/connection.py
+++ b/sanic/server/websockets/connection.py
@@ -45,7 +45,7 @@ class WebSocketConnection:
 
         await self._send(message)
 
-    async def recv(self, *args, **kwargs) -> Optional[str]:
+    async def recv(self, *args, **kwargs) -> Optional[Union[str, bytes]]:
         message = await self._receive()
 
         if message["type"] == "websocket.receive":
@@ -53,7 +53,7 @@ class WebSocketConnection:
                 return message["text"]
             except KeyError:
                 try:
-                    return message["bytes"].decode()
+                    return message["bytes"]
                 except KeyError:
                     raise InvalidUsage("Bad ASGI message received")
         elif message["type"] == "websocket.disconnect":

diff --git a/tests/test_asgi.py b/tests/test_asgi.py
index 0c76a67f..fe9ff306 100644
--- a/tests/test_asgi.py
+++ b/tests/test_asgi.py
@@ -342,7 +342,7 @@ async def test_websocket_send(send, receive, message_stack):
 
 
 @pytest.mark.asyncio
-async def test_websocket_receive(send, receive, message_stack):
+async def test_websocket_text_receive(send, receive, message_stack):
     msg = {"text": "hello", "type": "websocket.receive"}
     message_stack.append(msg)
 
@@ -351,6 +351,15 @@ async def test_websocket_receive(send, receive, message_stack):
 
     assert text == msg["text"]
 
+@pytest.mark.asyncio
+async def test_websocket_bytes_receive(send, receive, message_stack):
+    msg = {"bytes": b"hello", "type": "websocket.receive"}
+    message_stack.append(msg)
+
+    ws = WebSocketConnection(send, receive)
+    data = await ws.receive()
+
+    assert data == msg["bytes"]
 
 @pytest.mark.asyncio
 async def test_websocket_accept_with_no_subprotocols(

ASGI websocket must pass thru bytes as is


_Originally posted by @Tronic in https://github.com/sanic-org/sanic/pull/2640#discussion_r1058027028_
      
tests/test_asgi.py::test_websocket_bytes_receive
tests/test_asgi.py::test_mockprotocol_events tests/test_asgi.py::test_protocol_push_data tests/test_asgi.py::test_websocket_send tests/test_asgi.py::test_websocket_text_receive tests/test_asgi.py::test_websocket_accept_with_no_subprotocols tests/test_asgi.py::test_websocket_accept_with_subprotocol tests/test_asgi.py::test_websocket_accept_with_multiple_subprotocols tests/test_asgi.py::test_improper_websocket_connection tests/test_asgi.py::test_request_class_regular tests/test_asgi.py::test_request_class_custom tests/test_asgi.py::test_cookie_customization tests/test_asgi.py::test_content_type tests/test_asgi.py::test_request_handle_exception tests/test_asgi.py::test_request_exception_suppressed_by_middleware tests/test_asgi.py::test_signals_triggered tests/test_asgi.py::test_asgi_serve_location tests/test_asgi.py::test_error_on_lifespan_exception_start tests/test_asgi.py::test_error_on_lifespan_exception_stop