diff --git a/httpx/_models.py b/httpx/_models.py
index 865fd9a..683dde1 100644
--- a/httpx/_models.py
+++ b/httpx/_models.py
@@ -616,6 +616,9 @@ class Request:
         auto_headers: typing.List[typing.Tuple[bytes, bytes]] = []
 
         has_host = "host" in self.headers
+        has_content_length = (
+            "content-length" in self.headers or "transfer-encoding" in self.headers
+        )
         has_user_agent = "user-agent" in self.headers
         has_accept = "accept" in self.headers
         has_accept_encoding = "accept-encoding" in self.headers
@@ -626,6 +629,8 @@ class Request:
             if url.userinfo:
                 url = url.copy_with(username=None, password=None)
             auto_headers.append((b"host", url.authority.encode("ascii")))
+        if not has_content_length and self.method in ("POST", "PUT", "PATCH"):
+            auto_headers.append((b"content-length", b"0"))
         if not has_user_agent:
             auto_headers.append((b"user-agent", USER_AGENT.encode("ascii")))
         if not has_accept:

Content-Length header is missing in POST/DELETE/PUT/PATCH requests without body
### Checklist

<!-- To help keep this issue tracker clean and focused, please make sure you tried *all* the following resources before submitting your question. -->

- [x] I searched the [HTTPX documentation](https://www.python-httpx.org) but couldn't find what I'm looking for.
- [x] I looked through similar issues on GitHub, but didn't find anything.
- [x] I looked up "How to do ... in HTTPX" on a search engine and didn't find any information.
- [x] I asked the [community chat](https://gitter.im/encode/community) for help ~~but didn't get an answer.~~ [and decided to create an issue. :)](https://gitter.im/encode/community?at=5ec95c46778fad0b13201d03)

### Environment
**httpx version**: `0.13.1`
**Python version**: `3.7.3`
**OS**: Windows 7

### Question
I am not sure how to classify this issue: it could be a bug, since it is an incompatible with `requests`, on the other hand, it could be feature request, if you consider it is not that important. So, I decided to go with question. :D

Was replacing `requests` with `httpx` in my small script and received an error from the server `httpx._exceptions.HTTPError: 411 Client Error: Length Required for url: ...` while doing a `POST`  request **without a body** (same behavior is applicable for `PUT` and `PATCH` requests too).

*Steps to reproduce:*
```python
import requests
import httpx

for client in (requests, httpx):
    for method in ("get", "head", "delete", "post", "put", "patch"):
        r = client.request(method, f"https://httpbin.org/headers")
        print(
            f"[{client.__name__}] method={method.upper()} "
            f'Content-Length={r.request.headers.get("Content-Length")}'
        )
```

`requests` adds `Content-Length` header in every possible http method, except `GET` and `HEAD`.
`httpx` does not add `Content-Length` header at all, **unless requests has a body**.

#866 added `Content-Length` to all methods, but since it is not needed in `GET` and `HEAD` methods, it was reverted.

I assume, it should be handled during the `Request` building.
`httpx` decides whether to add `Content-Length` depending on the steam type, which is handled in [_content_streams.py#L314](https://github.com/encode/httpx/blob/aa630d36c22642d7004a57abd222a14461ee4d77/httpx/_content_streams.py#L372).

### Thoughts on how to solve it
To be honest, I am not sure what is the proper way to deal with this. :-(

My first idea was to use `method` inside the [stream](https://github.com/encode/httpx/blob/aa630d36c22642d7004a57abd222a14461ee4d77/httpx/_content_streams.py#L49):
```python3
class ByteStream(ContentStream):
    def __init__(self, body: typing.Union[str, bytes], method: str) -> None:
        self.body = body.encode("utf-8") if isinstance(body, str) else body
        self.method = method

    def get_headers(self) -> typing.Dict[str, str]:        
        if self.method in ("GET", "HEAD"):
            return {}
        content_length = str(len(self.body)) if self.body else "0"
        return {"Content-Length": content_length}
```

But this approach is not great, since it requires to pass `method` from `Request` to `encode` and then to `ByteStream` inside it. Also, it is not clear what `method` should I use when building stream in [`read`](https://github.com/encode/httpx/blob/aa630d36c22642d7004a57abd222a14461ee4d77/httpx/_models.py#L656) methods.

Please, let me know what you think. Thanks!
diff --git a/tests/client/test_client.py b/tests/client/test_client.py
index 1555690..0bb1cc1 100644
--- a/tests/client/test_client.py
+++ b/tests/client/test_client.py
@@ -37,6 +37,22 @@ def test_build_request(server):
     assert response.json()["Custom-header"] == "value"
 
 
+def test_build_post_request(server):
+    url = server.url.copy_with(path="/echo_headers")
+    headers = {"Custom-header": "value"}
+
+    with httpx.Client() as client:
+        request = client.build_request("POST", url)
+        request.headers.update(headers)
+        response = client.send(request)
+
+    assert response.status_code == 200
+    assert response.url == url
+
+    assert response.json()["Content-length"] == "0"
+    assert response.json()["Custom-header"] == "value"
+
+
 def test_post(server):
     with httpx.Client() as client:
         response = client.post(server.url, data=b"Hello, world!")

pip install -e .
parse_log_pytest
requirements.txt
pytest pytest-asyncio pytest-trio pytest-cov
apt-get update apt-get install -y gcc
3.8
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aspy.refactor-imports==3.0.2
async-generator==1.10
attrs==25.3.0
autoflake==2.3.1
babel==2.17.0
backports.tarfile==1.2.0
backrefs==5.7.post1
black==24.8.0
brotlipy==0.7.0
certifi==2025.1.31
cffi==1.17.1
chardet==3.0.4
charset-normalizer==3.4.1
click==8.1.8
colorama==0.4.6
coverage==7.6.1
cryptography==44.0.2
docutils==0.20.1
exceptiongroup==1.2.2
flake8==7.1.2
flake8-bugbear==24.12.12
flake8-pie==0.16.0
ghp-import==2.1.0
h11==0.9.0
h2==3.2.0
hpack==3.0.0
hstspreload==2025.1.1
httpcore==0.9.1
-e git+https://github.com/encode/httpx.git@66a45379594e5cf836e4743931119e76dc8f22c7#egg=httpx
hyperframe==5.2.0
id==1.5.0
idna==2.10
importlib_metadata==8.5.0
importlib_resources==6.4.5
iniconfig==2.1.0
isort==5.13.2
jaraco.classes==3.4.0
jaraco.context==6.0.1
jaraco.functools==4.1.0
jeepney==0.9.0
Jinja2==3.1.6
keyring==25.5.0
Markdown==3.7
markdown-it-py==3.0.0
MarkupSafe==2.1.5
mccabe==0.7.0
mdurl==0.1.2
mergedeep==1.3.4
mkautodoc==0.2.0
mkdocs==1.6.1
mkdocs-get-deps==0.2.0
mkdocs-material==9.6.10
mkdocs-material-extensions==1.3.1
more-itertools==10.5.0
mypy==1.14.1
mypy-extensions==1.0.0
nh3==0.2.21
outcome==1.3.0.post0
packaging==24.2
paginate==0.5.7
pathspec==0.12.1
platformdirs==4.3.6
pluggy==1.5.0
pycodestyle==2.12.1
pycparser==2.22
pyflakes==3.2.0
Pygments==2.19.1
pymdown-extensions==10.14.3
pytest==8.3.5
pytest-asyncio==0.24.0
pytest-cov==5.0.0
pytest-trio==0.8.0
python-dateutil==2.9.0.post0
pytz==2025.2
PyYAML==6.0.2
pyyaml_env_tag==0.1
readme_renderer==43.0
requests==2.32.3
requests-toolbelt==1.0.0
rfc3986==1.5.0
rich==14.0.0
SecretStorage==3.3.3
seed-isort-config==2.2.0
six==1.17.0
sniffio==1.3.1
sortedcontainers==2.4.0
tomli==2.2.1
trio==0.27.0
trio-typing==0.10.0
trustme==1.1.0
twine==6.1.0
typing_extensions==4.13.0
urllib3==2.2.3
uvicorn==0.33.0
watchdog==4.0.2
zipp==3.20.2

name: httpx
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
  - 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
  - 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
  - wheel=0.44.0=py38h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aspy-refactor-imports==3.0.2
      - async-generator==1.10
      - attrs==25.3.0
      - autoflake==2.3.1
      - babel==2.17.0
      - backports-tarfile==1.2.0
      - backrefs==5.7.post1
      - black==24.8.0
      - brotlipy==0.7.0
      - certifi==2025.1.31
      - cffi==1.17.1
      - chardet==3.0.4
      - charset-normalizer==3.4.1
      - click==8.1.8
      - colorama==0.4.6
      - coverage==7.6.1
      - cryptography==44.0.2
      - docutils==0.20.1
      - exceptiongroup==1.2.2
      - flake8==7.1.2
      - flake8-bugbear==24.12.12
      - flake8-pie==0.16.0
      - ghp-import==2.1.0
      - h11==0.9.0
      - h2==3.2.0
      - hpack==3.0.0
      - hstspreload==2025.1.1
      - httpcore==0.9.1
      - hyperframe==5.2.0
      - id==1.5.0
      - idna==2.10
      - importlib-metadata==8.5.0
      - importlib-resources==6.4.5
      - iniconfig==2.1.0
      - isort==5.13.2
      - jaraco-classes==3.4.0
      - jaraco-context==6.0.1
      - jaraco-functools==4.1.0
      - jeepney==0.9.0
      - jinja2==3.1.6
      - keyring==25.5.0
      - markdown==3.7
      - markdown-it-py==3.0.0
      - markupsafe==2.1.5
      - mccabe==0.7.0
      - mdurl==0.1.2
      - mergedeep==1.3.4
      - mkautodoc==0.2.0
      - mkdocs==1.6.1
      - mkdocs-get-deps==0.2.0
      - mkdocs-material==9.6.10
      - mkdocs-material-extensions==1.3.1
      - more-itertools==10.5.0
      - mypy==1.14.1
      - mypy-extensions==1.0.0
      - nh3==0.2.21
      - outcome==1.3.0.post0
      - packaging==24.2
      - paginate==0.5.7
      - pathspec==0.12.1
      - platformdirs==4.3.6
      - pluggy==1.5.0
      - pycodestyle==2.12.1
      - pycparser==2.22
      - pyflakes==3.2.0
      - pygments==2.19.1
      - pymdown-extensions==10.14.3
      - pytest==8.3.5
      - pytest-asyncio==0.24.0
      - pytest-cov==5.0.0
      - pytest-trio==0.8.0
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - pyyaml==6.0.2
      - pyyaml-env-tag==0.1
      - readme-renderer==43.0
      - requests==2.32.3
      - requests-toolbelt==1.0.0
      - rfc3986==1.5.0
      - rich==14.0.0
      - secretstorage==3.3.3
      - seed-isort-config==2.2.0
      - six==1.17.0
      - sniffio==1.3.1
      - sortedcontainers==2.4.0
      - tomli==2.2.1
      - trio==0.27.0
      - trio-typing==0.10.0
      - trustme==1.1.0
      - twine==6.1.0
      - typing-extensions==4.13.0
      - urllib3==2.2.3
      - uvicorn==0.33.0
      - watchdog==4.0.2
      - zipp==3.20.2
prefix: /opt/conda/envs/httpx

tests/client/test_client.py::test_build_post_request
tests/client/test_client.py::test_get tests/client/test_client.py::test_build_request tests/client/test_client.py::test_post tests/client/test_client.py::test_post_json tests/client/test_client.py::test_stream_response tests/client/test_client.py::test_stream_iterator tests/client/test_client.py::test_raw_iterator tests/client/test_client.py::test_raise_for_status tests/client/test_client.py::test_options tests/client/test_client.py::test_head tests/client/test_client.py::test_put tests/client/test_client.py::test_patch tests/client/test_client.py::test_delete tests/client/test_client.py::test_base_url tests/client/test_client.py::test_merge_url
httpx/_models.py
httpx/_models.py