diff --git a/respx/mock.py b/respx/mock.py
index 39d4176..c3f45e5 100644
--- a/respx/mock.py
+++ b/respx/mock.py
@@ -108,6 +108,7 @@ class HTTPXMock:
         content: typing.Optional[ContentDataTypes] = None,
         content_type: typing.Optional[str] = None,
         headers: typing.Optional[HeaderTypes] = None,
+        pass_through: bool = False,
         alias: typing.Optional[str] = None,
     ) -> RequestPattern:
         """
@@ -118,7 +119,9 @@ class HTTPXMock:
             headers["Content-Type"] = content_type
 
         response = ResponseTemplate(status_code, headers, content)
-        pattern = RequestPattern(method, url, response, alias=alias)
+        pattern = RequestPattern(
+            method, url, response, pass_through=pass_through, alias=alias
+        )
 
         self.add(pattern, alias=alias)
 
@@ -145,8 +148,8 @@ class HTTPXMock:
         matched_response: typing.Optional[ResponseTemplate] = None
 
         for i, pattern in enumerate(self._patterns):
-            response = pattern.match(request)
-            if not response:
+            match = pattern.match(request)
+            if not match:
                 continue
 
             if found_index is not None:
@@ -156,38 +159,60 @@ class HTTPXMock:
 
             found_index = i
             matched_pattern = pattern
-            matched_response = response
+
+            if isinstance(match, ResponseTemplate):
+                # Mock response
+                matched_response = match
+            elif isinstance(match, AsyncRequest):
+                # Pass-through request
+                matched_response = None
+            else:
+                raise ValueError(
+                    (
+                        "Matched request pattern must return either a "
+                        'ResponseTemplate or an AsyncResponse, got "{}"'
+                    ).format(type(match))
+                )
 
         return matched_pattern, matched_response
 
     @contextmanager
-    def _patch_backend(self, backend: ConcurrencyBackend) -> typing.Iterator[None]:
+    def _patch_backend(
+        self,
+        backend: ConcurrencyBackend,
+        request: AsyncRequest,
+        response: typing.Optional[ResponseTemplate],
+    ) -> typing.Iterator[None]:
         patchers = []
 
-        # Mock open_tcp_stream()
-        patchers.append(
-            asynctest.mock.patch.object(
-                backend, "open_tcp_stream", self._open_tcp_stream_mock
-            )
-        )
+        if response is not None:
+            # 1. Patch request url with response for later pickup in patched backend
+            request.url = URLResponse(request.url, response)
 
-        # Mock open_uds_stream()
-        # TODO: Remove if-statement once httpx uds support is released
-        if hasattr(backend, "open_uds_stream"):  # pragma: nocover
+            # Mock open_tcp_stream()
             patchers.append(
                 asynctest.mock.patch.object(
-                    backend, "open_uds_stream", self._open_uds_stream_mock
+                    backend, "open_tcp_stream", self._open_tcp_stream_mock
                 )
             )
 
-        # Start patchers
+            # Mock open_uds_stream()
+            # TODO: Remove if-statement once httpx uds support is released
+            if hasattr(backend, "open_uds_stream"):  # pragma: nocover
+                patchers.append(
+                    asynctest.mock.patch.object(
+                        backend, "open_uds_stream", self._open_uds_stream_mock
+                    )
+                )
+
+        # 2. Start patchers
         for patcher in patchers:
             patcher.start()
 
         try:
             yield
         finally:
-            # Stop patchers
+            # 3. Stop patchers
             for patcher in patchers:
                 patcher.start()
 
@@ -203,17 +228,17 @@ class HTTPXMock:
         # 1. Match request against added patterns
         pattern, template = self._match(request)
 
-        # 2. Patch request url with response for later pickup in mocked backend methods
-        request.url = URLResponse(request.url, template or ResponseTemplate())
+        if pattern is None:
+            template = ResponseTemplate()
 
-        # 3. Patch client's backend and pass-through to _get_response
+        # 2. Patch client's backend and continue to original _get_response
         try:
             global _get_response
-            with self._patch_backend(client.concurrency_backend):
+            with self._patch_backend(client.concurrency_backend, request, template):
                 response = None
                 response = await _get_response(client, request, **kwargs)
         finally:
-            # 4. Update stats
+            # 3. Update stats
             if pattern:
                 pattern(request, response)
             self.calls.append((request, response))
diff --git a/respx/models.py b/respx/models.py
index dc32211..5131247 100644
--- a/respx/models.py
+++ b/respx/models.py
@@ -74,6 +74,7 @@ class RequestPattern:
         method: typing.Union[str, typing.Callable],
         url: typing.Optional[typing.Union[str, typing.Pattern]],
         response: ResponseTemplate,
+        pass_through: bool = False,
         alias: typing.Optional[str] = None,
     ) -> None:
         self._match_func: typing.Optional[typing.Callable] = None
@@ -81,10 +82,12 @@ class RequestPattern:
         if callable(method):
             self.method = None
             self.url = None
+            self.pass_through = None
             self._match_func = method
         else:
             self.method = method
             self.url = url
+            self.pass_through = pass_through
 
         self.response = response
         self.alias = alias
@@ -106,10 +109,22 @@ class RequestPattern:
     ) -> None:
         self._stats(request, response)
 
-    def match(self, request: AsyncRequest) -> typing.Optional[ResponseTemplate]:
+    def match(
+        self, request: AsyncRequest
+    ) -> typing.Optional[typing.Union[AsyncRequest, ResponseTemplate]]:
+        """
+        Matches request with configured pattern;
+        custom matcher function or http method + url pattern.
+
+        Returns None for a non-matching pattern, mocked response for a match,
+        or input request for pass-through.
+        """
         matches = False
         url_params: Kwargs = {}
 
+        if self.pass_through:
+            return request
+
         if self._match_func:
             response = self.response.clone(context={"request": request})
             return self._match_func(request, response)

Pass-through requests
It would sometimes be useful to mark a pattern to pass-through the request to the original url.

**Ideas:**
```py
with respx.mock():
    respx.get(...., pass_through=True)
    # -- and/or --
    respx.get(...., pass_through=pass_through_or_not)

def pass_through_or_not(request, **kwargs):
    if some_awesome_logic:
        return True
```

Relates to #16 
diff --git a/tests/test_mock.py b/tests/test_mock.py
index 6870958..9624c47 100644
--- a/tests/test_mock.py
+++ b/tests/test_mock.py
@@ -309,8 +309,8 @@ class HTTPXMockTestCase(asynctest.TestCase):
                 response.context["id"] = 123
                 return response
 
-        with respx.HTTPXMock() as httpx_mock:
-            httpx_mock.request(matcher, status_code=202, headers={"X-Ham": "Spam"})
+        with respx.HTTPXMock(assert_all_called=False) as httpx_mock:
+            request = httpx_mock.request(matcher, status_code=202, headers={"X-Ham": "Spam"})
             response = httpx.get("https://foo/bar/")
 
             self.assertEqual(response.status_code, 202)
@@ -319,6 +319,12 @@ class HTTPXMockTestCase(asynctest.TestCase):
                 httpx.Headers({"Content-Type": "text/plain", "X-Ham": "Spam"}),
             )
             self.assertEqual(response.text, "foobar #123")
+            self.assertTrue(request.called)
+            self.assertFalse(request.pass_through)
+
+            with self.assertRaises(ValueError):
+                httpx_mock.request(lambda req, res: "invalid")
+                httpx.get("https://ham/spam/")
 
     def test_assert_all_called_fail(self):
         with self.assertRaises(AssertionError):
@@ -355,6 +361,36 @@ class HTTPXMockTestCase(asynctest.TestCase):
             self.assertTrue(request1.called)
             self.assertTrue(request2.called)
 
+    def test_pass_through_with_arg(self):
+        with respx.mock():
+            request = respx.get("https://www.example.org/", pass_through=True)
+
+            with asynctest.mock.patch(
+                "asyncio.open_connection",
+                side_effect=ConnectionRefusedError("test request blocked"),
+            ) as open_connection:
+                with self.assertRaises(ConnectionRefusedError):
+                    httpx.get("https://www.example.org/")
+
+            self.assertTrue(open_connection.called)
+            self.assertTrue(request.called)
+            self.assertTrue(request.pass_through)
+
+    def test_pass_through_with_custom_matcher(self):
+        with respx.mock():
+            request = respx.request(lambda request, response: request)
+
+            with asynctest.mock.patch(
+                "asyncio.open_connection",
+                side_effect=ConnectionRefusedError("test request blocked"),
+            ) as open_connection:
+                with self.assertRaises(ConnectionRefusedError):
+                    httpx.get("https://www.example.org/")
+
+            self.assertTrue(open_connection.called)
+            self.assertTrue(request.called)
+            self.assertIsNone(request.pass_through)
+
     async def test_stats(self, backend=None):
         with respx.mock():
             url = "https://foo/bar/1/"

pip install -e .
parse_log_pytest
pytest
pytest pytest-asyncio pytest-cov trio
3.8
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
asynctest==0.13.0
attrs==25.3.0
certifi==2025.1.31
chardet==3.0.4
coverage==7.6.1
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
h11==0.8.1
h2==3.2.0
hpack==3.0.0
hstspreload==2025.1.1
httpx==0.7.6
hyperframe==5.2.0
idna==2.10
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
outcome==1.3.0.post0
packaging @ file:///croot/packaging_1720101850331/work
pluggy==1.5.0
pytest==8.3.5
pytest-asyncio==0.24.0
pytest-cov==5.0.0
-e git+https://github.com/lundberg/respx.git@05747d93cdac6c9c91e909f63c95bd56410da15b#egg=respx
rfc3986==1.5.0
sniffio==1.3.1
sortedcontainers==2.4.0
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
trio==0.27.0

name: respx
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py38h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.1=py38h06a4308_0
  - pip=24.2=py38h06a4308_0
  - python=3.8.20=he870216_0
  - readline=8.2=h5eee18b_0
  - setuptools=75.1.0=py38h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py38h06a4308_0
  - wheel=0.44.0=py38h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - asynctest==0.13.0
      - attrs==25.3.0
      - certifi==2025.1.31
      - chardet==3.0.4
      - coverage==7.6.1
      - h11==0.8.1
      - h2==3.2.0
      - hpack==3.0.0
      - hstspreload==2025.1.1
      - httpx==0.7.6
      - hyperframe==5.2.0
      - idna==2.10
      - outcome==1.3.0.post0
      - pluggy==1.5.0
      - pytest==8.3.5
      - pytest-asyncio==0.24.0
      - pytest-cov==5.0.0
      - rfc3986==1.5.0
      - sniffio==1.3.1
      - sortedcontainers==2.4.0
      - trio==0.27.0
prefix: /opt/conda/envs/respx

tests/test_mock.py::HTTPXMockTestCase::test_custom_matcher tests/test_mock.py::HTTPXMockTestCase::test_pass_through_with_arg tests/test_mock.py::HTTPXMockTestCase::test_pass_through_with_custom_matcher
tests/test_mock.py::HTTPXMockTestCase::test_alias tests/test_mock.py::HTTPXMockTestCase::test_api tests/test_mock.py::HTTPXMockTestCase::test_assert_all_called_disabled tests/test_mock.py::HTTPXMockTestCase::test_assert_all_called_fail tests/test_mock.py::HTTPXMockTestCase::test_assert_all_called_sucess tests/test_mock.py::HTTPXMockTestCase::test_async_client tests/test_mock.py::HTTPXMockTestCase::test_callable_content tests/test_mock.py::HTTPXMockTestCase::test_content_type tests/test_mock.py::HTTPXMockTestCase::test_exception tests/test_mock.py::HTTPXMockTestCase::test_headers tests/test_mock.py::HTTPXMockTestCase::test_http_methods tests/test_mock.py::HTTPXMockTestCase::test_invalid_url_pattern tests/test_mock.py::HTTPXMockTestCase::test_json_content tests/test_mock.py::HTTPXMockTestCase::test_mock_contextmanager tests/test_mock.py::HTTPXMockTestCase::test_mock_decorator tests/test_mock.py::HTTPXMockTestCase::test_raising_content tests/test_mock.py::HTTPXMockTestCase::test_raw_content tests/test_mock.py::HTTPXMockTestCase::test_regex_url_pattern tests/test_mock.py::HTTPXMockTestCase::test_repeated_pattern tests/test_mock.py::HTTPXMockTestCase::test_stats tests/test_mock.py::HTTPXMockTestCase::test_status_code tests/test_mock.py::HTTPXMockTestCase::test_string_url_pattern tests/test_mock.py::HTTPXMockTestCase::test_sync_client tests/test_mock.py::HTTPXMockTestCase::test_trio_backend tests/test_mock.py::HTTPXMockTestCase::test_unknown_url
respx/models.py respx/mock.py
respx/models.py respx/mock.py