# Enabling ECH (Encrypted Client Hello) with nginx 1.29.4 using nginx-build

**URL:** <https://community.nginx.org/t/enabling-ech-encrypted-client-hello-with-nginx-1-29-4-using-nginx-build/9064>\
**Category:** Community Showcase\
**Tags:** security, openssl\
**Created:** [February 23, 2026, 7:44am UTC](https://community.nginx.org/t/enabling-ech-encrypted-client-hello-with-nginx-1-29-4-using-nginx-build/9064 "2026-02-23T07:44:35Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![catatsuy](https://drob5swvtjqxg.cloudfront.net/user_avatar/community.nginx.org/catatsuy/32/240_2.png) [@catatsuy](https://community.nginx.org/u/catatsuy)\
**Post date:** [February 23, 2026, 7:44am UTC](https://community.nginx.org/t/enabling-ech-encrypted-client-hello-with-nginx-1-29-4-using-nginx-build/9064/1 "2026-02-23T07:44:35Z")

</div>

nginx 1.29.4 added support for ECH (Encrypted Client Hello).

To use ECH with nginx, you currently need an OpenSSL branch with ECH support.  
A while ago (since `nginx-build` v0.16.0), I added support for using a custom OpenSSL-compatible implementation in `nginx-build` (I am the maintainer), so I used that feature for this setup.

> **[GitHub - cubicdaiya/nginx-build: Seamless nginx builder](https://github.com/cubicdaiya/nginx-build)**
>
> Seamless nginx builder

The rough flow is:

- Build nginx itself with `nginx-build`
- Build ECH-enabled OpenSSL in Docker
- Add `ech` to the DNS HTTPS record
- Confirm `ech:SUCCESS` in nginx access logs

The official nginx blog post explains the overall ECH flow very well:

> **[Encrypted Client Hello Comes to NGINX – NGINX Community Blog](https://blog.nginx.org/blog/encrypted-client-hello-comes-to-nginx)**

## Build ECH-enabled nginx with nginx-build

`nginx-build` supports `-customssl`, `-customsslname`, and `-customssltag` (I added these recently).

First, prepare a `configure` script like this:

```bash
#!/bin/sh
./configure \
  --sbin-path=/usr/local/nginx/sbin/nginx \
  --conf-path=/usr/local/nginx/conf/nginx.conf \
  --pid-path=/usr/local/nginx/logs/nginx.pid \
  --with-http_ssl_module \
  --with-http_v3_module \
  --with-stream_ssl_module

```

Then run `nginx-build` like this:

```bash
nginx-build \
  -d work \
  -v 1.29.4 \
  -c ./configure.ech.sh \
  -customssl https://github.com/openssl/openssl.git \
  -customsslname openssl-ech \
  -customssltag feature/ech

```

This builds nginx 1.29.4 with OpenSSL `feature/ech`.

## Use `openssl ech` via Docker

At the moment, the normal upstream OpenSSL build does not provide `openssl ech`, so I build an ECH-enabled OpenSSL in Docker.

```Dockerfile
FROM debian:bookworm

RUN apt-get update && apt-get install -y \
    build-essential \
    git \
    ca-certificates \
    perl \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /build
RUN git clone --depth=1 --branch feature/ech https://github.com/openssl/openssl.git openssl

WORKDIR /build/openssl
RUN ./Configure --prefix=/opt/openssl-ech --openssldir=/opt/openssl-ech/ssl \
 && make -j"$(nproc)" \
 && make install_sw

ENV PATH="/opt/openssl-ech/bin:${PATH}"
ENV LD_LIBRARY_PATH="/opt/openssl-ech/lib64:/opt/openssl-ech/lib"

WORKDIR /work
CMD ["bash"]

```

Build the image:

```bash
docker build -t openssl-ech .

```

Then run `openssl ech` like this:

```bash
docker run --rm \
  openssl-ech \
  openssl ech -public_name ech.catatsuy.org -out /dev/stdout

```

The domain passed to `-public_name` becomes the **outer name** (public name), so it must be a domain you control and for which you have a valid certificate.

This prints a PEM-like output to stdout. I copy it and save it as a file for nginx:

```pem
-----BEGIN PRIVATE KEY-----
aaaaaaaa
-----END PRIVATE KEY-----
-----BEGIN ECHCONFIG-----
bbbb
cccc
-----END ECHCONFIG-----

```

## Add ECHCONFIG to the DNS HTTPS record

The content between `BEGIN ECHCONFIG` and `END ECHCONFIG` is base64.

When adding it to DNS as `ech=`, remove line breaks and make it a single line.

In other words, add something like this to your DNS HTTPS record:

```plaintext
ech="bbbbcccc"

```

## nginx configuration

Add `ssl_ech_file` and log `$ssl_ech_status` and `$ssl_ech_outer_server_name`.

```nginx
http {
  ssl_ech_file /usr/local/nginx/conf/ech/ech.catatsuy.org.pem.ech;

  log_format main '$remote_addr - $host "$request" $status '
    'ech:$ssl_ech_status:$ssl_ech_outer_server_name';

  access_log /usr/local/nginx/logs/access.log main;

  # Outer side (public_name side)
  server {
    listen 443 ssl http2;
    server_name ech.catatsuy.org;
    ssl_certificate /path/to/ech.catatsuy.org.crt;
    ssl_certificate_key /path/to/ech.catatsuy.org.key;
  }

  # Real service
  server {
    listen 443 ssl http2;
    server_name www.catatsuy.org;
    ssl_certificate /path/to/www.catatsuy.org.crt;
    ssl_certificate_key /path/to/www.catatsuy.org.key;
  }
}

```

## Verification

Check the nginx access log. If you see output like this, it worked:

```plaintext
ech:SUCCESS:ech.catatsuy.org

```

## Summary

The full flow was:

- Build nginx 1.29.4 + OpenSSL `feature/ech` with `nginx-build`
- Use Docker `openssl-ech` image to run `openssl ech`
- Extract ECHCONFIG and add it to the DNS HTTPS record (`ech=`)
- Add `ssl_ech_file` and ECH-related access log fields in nginx
- Confirm `ech:SUCCESS:ech.catatsuy.org` in access logs

It is a little inconvenient that this still needs the OpenSSL ECH branch, but with `nginx-build` it is pretty easy to try.

---

<div class="post-metadata">

**Author:** ![buulam](https://drob5swvtjqxg.cloudfront.net/user_avatar/community.nginx.org/buulam/32/3733_2.png) [@buulam](https://community.nginx.org/u/buulam)\
**Post date:** [February 25, 2026, 2:49pm UTC](https://community.nginx.org/t/enabling-ech-encrypted-client-hello-with-nginx-1-29-4-using-nginx-build/9064/2 "2026-02-25T14:49:34Z")

</div>

Thanks for sharing! A lot of people are really excited about this one!
