wasmerio/wasmer · error

Could not apply request header: '{name}': '{value}'

Error message

Could not apply request header: '{name}': '{value}'

What it means

In the browser/web HTTP client for WASIX, `fetch` copies each request header onto a JS `Headers`-like object via set(). If the JS side rejects a header (browser fetch forbids certain headers like Host, Content-Length, Connection; or invalid characters in name/value), js_error is turned into this context message naming the offending header. The request never reaches the network.

Source

Thrown at lib/wasix/src/http/web_http_client.rs:201

            // is configured then try again with the cors proxy
            let url = if let Some(cors_proxy) = cors_proxy {
                format!("https://{}/{}", cors_proxy, url)
            } else {
                return Err(js_error(e).context(format!("Could not fetch '{url}'")));
            };

            let request = web_sys::Request::new_with_str_and_init(&url, &opts)
                .map_err(js_error)
                .with_context(|| format!("Could not construct request for url '{url}'"))?;

            let set_headers = request.headers();
            for (name, val) in headers.iter() {
                let value = String::from_utf8_lossy(val.as_bytes());
                set_headers
                    .set(name.as_str(), &value)
                    .map_err(js_error)
                    .with_context(|| {
                        anyhow::anyhow!("Could not apply request header: '{name}': '{value}'")
                    })?;
            }

            call_fetch(&request)
                .await
                .map_err(js_error)
                .with_context(|| format!("Could not fetch '{url}'"))?
        }
    };

    let response = resp_value.dyn_ref().unwrap();
    read_response(response).await
}

async fn read_response(response: &web_sys::Response) -> Result<HttpResponse, anyhow::Error> {
    let status = http::StatusCode::from_u16(response.status())?;
    let headers = headers(response.headers()).context("Unable to read the headers")?;
    let body = get_response_data(response).await?;

View on GitHub (pinned to 8c4b9ee9d3)

Solutions

  1. Remove forbidden headers before fetch: drop Host, Content-Length, Connection, Transfer-Encoding and similar from the request.
  2. Sanitize header values: strip control characters and ensure UTF-8-safe ASCII values.
  3. Only forward safe headers when proxying an inbound request (use an allowlist).
  4. Check the inner js_error text — browsers list the exact rejected header.
  5. If the header is genuinely required, move it to the server/proxy layer instead of the browser fetch.

Example fix

// before
for (name, val) in headers.iter() {
    let value = String::from_utf8_lossy(val.as_bytes());
    set_headers.set(name.as_str(), &value).map_err(js_error)?;
}
// after: skip forbidden headers
const FORBIDDEN: &[&str] = &["host", "content-length", "connection", "transfer-encoding"];
for (name, val) in headers.iter() {
    if FORBIDDEN.contains(&name.as_str().to_ascii_lowercase().as_str()) { continue; }
    let value: String = String::from_utf8_lossy(val.as_bytes())
        .chars().filter(|c| !c.is_control()).collect();
    set_headers.set(name.as_str(), &value).map_err(js_error)?;
}
Defensive patterns

Strategy: validation

Validate before calling

// filter forbidden/malformed headers before calling fetch
const FORBIDDEN: &[&str] = &["host", "content-length", "connection", "transfer-encoding", "keep-alive"];
fn is_safe_header(name: &str, value: &str) -> bool {
    !FORBIDDEN.contains(&name.to_ascii_lowercase().as_str())
        && value.chars().all(|c| !c.is_control())
        && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')
}

Try / catch

match spawn_fetch(req).await {
    Err(e) if e.to_string().contains("Could not apply request header") => {
        eprintln!("{e}\nHint: browsers forbid Host/Content-Length/Connection headers — strip them before fetch");
        std::process::exit(1);
    }
    other => other,
}

Prevention

When it happens

Trigger: A WASM module sets a forbidden or malformed header (e.g. Host, Content-Length, Connection, or a value containing non-ASCII/control characters) and the underlying JS set() throws.

Common situations: Ported native code that manually sets Host or Content-Length; user-supplied header values with newlines or unicode; forwarding an incoming request's full header map (including hop-by-hop headers) to a new fetch call.

Related errors


AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01). Data as JSON: /api/errors/d49d93f25bc514a9. Report an issue: GitHub.