# What ECH Status Information Is Available in CFnew Response Headers

> Discover ECH status information in CFnew response headers. Learn how X-ECH-Status and X-ECH-Config-Length headers reveal ECH configuration details for enhanced privacy.

- Repository: [byJoey/cfnew](https://github.com/byJoey/cfnew)
- Tags: api-reference
- Published: 2026-05-23

---

**CFnew exposes Encrypted Client Hello (ECH) status through the custom HTTP headers `X-ECH-Status` and `X-ECH-Config-Length`, which are added to responses when ECH is enabled and a configuration is successfully retrieved.**

The `byJoey/cfnew` repository implements ECH support within its Cloudflare Worker architecture, providing real-time visibility into TLS handshake encryption states via response metadata. These headers allow client applications, monitoring tools, and the built-in UI to verify whether ECH protection is active and validate the size of the cryptographic configuration fetched from DNS-over-HTTPS (DoH) resolvers.

## Available ECH Response Headers

CFnew conditionally injects ECH-related headers based on the global `enableECH` flag and the presence of a valid `echConfig` object. The current implementation emits two functional headers, with a third documented but inactive.

### X-ECH-Status Header

The **`X-ECH-Status`** header appears only when the `enableECH` configuration option is set to `true`. Its value is always the string `ENABLED`, serving as a boolean indicator that the Encrypted Client Hello feature is active for the current request. When ECH is disabled or the feature flag is off, this header is omitted entirely rather than returning a negative value.

### X-ECH-Config-Length Header

The **`X-ECH-Config-Length`** header contains the length of the binary ECH configuration as a decimal string (e.g., `"32"`). This header is injected only when both `enableECH` is true and a valid `echConfig` has been successfully retrieved from the DoH resolver. The length value corresponds to `echConfig.length` in the Worker source, providing a quick sanity check that configuration data was actually fetched and parsed.

### X-ECH-Debug Header (Documented but Not Implemented)

While the README references a **`X-ECH-Debug`** header intended to carry detailed troubleshooting strings (such as `"SUCCESS"` or `"FAILED"`), the current source code in the main branch does not emit this header. It remains a planned feature in the documented contract but should not be expected in production responses.

## How CFnew Injects ECH Headers

The header injection logic resides in the main Worker script (`明文源吗`), specifically within the response construction block. The code builds a headers object dynamically, appending ECH metadata only when the prerequisite conditions are met:

```js
// In src/明文源吗 (lines 2769-2774)
const responseHeaders = {
    'Content-Type': contentType,
    'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=0',
};
if (enableECH) {
    responseHeaders['X-ECH-Status'] = 'ENABLED';
    if (echConfig) {
        responseHeaders['X-ECH-Config-Length'] = String(echConfig.length);
    }
}

```

This implementation ensures that header consumers can rely on the presence of `X-ECH-Config-Length` as a secondary confirmation that ECH configuration retrieval succeeded, not just that the feature flag is enabled.

## Consuming ECH Headers in Client Applications

Front-end and programmatic clients can read these headers to conditionally handle ECH-enabled connections or display status indicators.

### Fetching ECH Status with Node.js

Use the standard `fetch` API to inspect headers before processing the subscription payload:

```js
fetch('https://your-worker.example.com/sub')
  .then(response => {
    const echStatus = response.headers.get('X-ECH-Status');
    const configLength = response.headers.get('X-ECH-Config-Length');
    
    console.log('ECH Enabled:', echStatus === 'ENABLED');
    console.log('Config Size:', configLength ? `${configLength} bytes` : 'N/A');
    
    return response.text();
  })
  .then(data => console.log('Subscription:', data))
  .catch(err => console.error('Request failed:', err));

```

### Using Headers in Downstream Cloudflare Workers

When chaining CFnew behind another Worker, inspect the headers to implement conditional routing or logging:

```js
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const resp = await fetchSubscription(request);
  const echStatus = resp.headers.get('X-ECH-Status');
  
  if (echStatus === 'ENABLED') {
    const configSize = resp.headers.get('X-ECH-Config-Length');
    console.log(`ECH active with ${configSize} bytes of config`);
    // Implement ECH-specific logic here
  }
  
  return resp;
}

```

### Displaying Status in Browser Applications

The built-in UI in `明文源吗` (lines 6310-6315) demonstrates client-side header consumption:

```js
const echStatusHeader = response.headers.get('X-ECH-Status');
const echConfigLength = response.headers.get('X-ECH-Config-Length');

if (echStatusHeader === 'ENABLED') {
    echStatusEl.innerHTML = `ECH状态: <span style="color:#00ff9d;">✅ 已启用${echConfigLength ? ' (配置长度: ' + echConfigLength + ')' : ''}</span>`;
} else {
    echStatusEl.innerHTML = 'ECH状态: <span style="color:#ffb400;">⚠️ 未启用</span>';
}

```

A minimal standalone implementation:

```html
<!DOCTYPE html>
<html>
<body>
  <pre id="echInfo">Loading…</pre>
  <script>
    fetch('/sub')
      .then(r => {
        const status = r.headers.get('X-ECH-Status') || 'DISABLED';
        const length = r.headers.get('X-ECH-Config-Length') || '-';
        document.getElementById('echInfo').textContent =
          `ECH status: ${status}\nECH config length: ${length}`;
      })
      .catch(err => console.error(err));
  </script>
</body>
</html>

```

## Summary

- **CFnew emits two functional ECH headers**: `X-ECH-Status` (value: `ENABLED`) and `X-ECH-Config-Length` (decimal string of byte count).
- **Headers are conditional**: They appear only when `enableECH` is true and, for the length header, when `echConfig` is successfully populated.
- **Source location**: Header injection occurs in `明文源吗` around lines 2769-2774, with UI consumption logic at lines 6310-6315.
- **X-ECH-Debug is placeholder**: Documented in the README but not currently implemented in the Worker code.
- **Consumption pattern**: Check for header presence using `headers.get()`, as omitted headers indicate disabled or unconfigured ECH states.

## Frequently Asked Questions

### What value does the X-ECH-Status header return when ECH is disabled?

When ECH is disabled, the `X-ECH-Status` header is **omitted entirely** from the response. The code only adds this header inside the `if (enableECH)` block, so its absence indicates that the global ECH flag is false or the feature is not configured.

### How can I verify that the ECH configuration was actually fetched?

Check for the presence of the `X-ECH-Config-Length` header. This header is only added when the `echConfig` variable contains data, meaning the DNS-over-HTTPS lookup succeeded and returned valid ECH configuration bytes. If only `X-ECH-Status` appears without the length header, ECH is enabled but configuration retrieval failed.

### Why doesn't the X-ECH-Debug header appear in my responses?

The `X-ECH-Debug` header is **documented in the README** as part of the design specification but is **not emitted by the current implementation** in `明文源吗`. The active code only generates `X-ECH-Status` and `X-ECH-Config-Length`, making debug information currently unavailable in standard responses.

### Which file contains the logic for adding ECH headers to responses?

The header injection logic is located in the **`明文源吗`** file (the main Worker script) at approximately lines 2769-2774, where the response headers object is constructed conditionally based on the `enableECH` flag and `echConfig` availability.