Skip to content

wasi-http: don't send forbidden headers#136

Closed
pchickey wants to merge 3 commits into
mainfrom
pch/forbidden_headers
Closed

wasi-http: don't send forbidden headers#136
pchickey wants to merge 3 commits into
mainfrom
pch/forbidden_headers

Conversation

@pchickey

@pchickey pchickey commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Everyone agrees wasi shouldnt use forbidden headers, but nobody agrees on what they are (yet) WebAssembly/WASI#940.

This PR takes the default wasmtime set, and adds Content-Length and Expect as forbidden headers. Because HeaderMap doesn't provide us a mechanism to forbid headers, we instead just silently drop any of the known forbidden headers when translating to a wasi fields resource.

The set of forbidden headers is:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • Proxy-Connection
  • Transfer-Encoding
  • Upgrade
  • Host
  • Http2-Settings
  • Expect
  • Content-Length

Since this is a pretty significant behavioral change, we will bump the version to 0.7.0 for the next release after this lands.

pchickey added 2 commits July 17, 2026 14:39
the set of FORBIDDEN_HEADERS is derived from wasmtime, with the addition
of Content-Length, and Expect.
@yoshuawuyts

yoshuawuyts commented Jul 17, 2026

Copy link
Copy Markdown
Member

Everyone agrees wasi shouldnt use forbidden headers, but nobody agrees on what they are (yet)

I'm surprised to hear you say that; years ago I created a table indexing all HTTP headers, and I definitely sourced the forbidden information from somewhere. I think it was MDN, but I no longer see it on there. Here is what that table looks like, though it is now five years out of date. See the last column of the table for the forbidden tracker:

Screenshot 2026-07-18 at 00 58 36

I saw for example the Date header in the table as forbidden that you didn't mention here. The fetch spec in the browser for sure has a list of forbidden headers. If that's what we're trying to follow, I think we should probably dig into the spec(s) to arrive at our answers.

@yoshuawuyts

yoshuawuyts commented Jul 17, 2026

Copy link
Copy Markdown
Member

Oh hah, the algorithm is documented here: https://fetch.spec.whatwg.org/#forbidden-request-header. That was easier to find than expected. I think we should probably just adopt this?

@yoshuawuyts

Copy link
Copy Markdown
Member

By the way, we might even want to upstream this to wasi:http proper. If we want to be portable to the browser too, then this might be useful to enforce as part of the portability guarantees?

@pchickey

pchickey commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Yes, whatwg has published that spec of forbidden headers. The trouble with adopting that list wholesale is that several of those restrictions are Web-platform specific and would be harmful to forbid in a proxy setting, specifically Cookie, Cookie2, DNT, Referer, Set-Cookie, and Via.

I don't really know one way or another whether the CORS Access-Control- headers, or the Proxy- and Sec-, should be forbidden or not.

We also need a way to set TE to Trailers as part of the scheme to detect that http2+ is required, I believe - thats a hack to make it possible to do grpc, something I'm not sure if any wasi-http implementation has ever followed through on...

Comment thread src/http/server.rs
Comment on lines -47 to -54
// Automatically add a Content-Length header.
if let Some(len) = body.content_length() {
let mut buffer = itoa::Buffer::new();
wasi_headers
.append(CONTENT_LENGTH.as_str(), buffer.format(len).as_bytes())
.unwrap();
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised this works? I'm looking at Jco's http.ts here, and this might cause problems for it? I'd feel a lot better if we had a test for this in wasi-testsuite to greenlight the behavior before we land it here.

@yoshuawuyts

yoshuawuyts commented Jul 19, 2026

Copy link
Copy Markdown
Member

@pchickey Oh heh, I forgot that this was specifically for the proxy worlds. Since we renamed the world from proxy to middleware, I've started thinking of wasi:http as something more general. But clearly there is a tension between running things in the browser vs in a proxy vs on a server.

What I'd like for this PR is for us to document somewhere what we are deciding to do, and why. Ideally that would be upstream in wasi:http, but I'm also happy if we do that here in wstd first. The bar I'm trying to clear for myself is that if someone asks me about this decision, I'm also able to explain it to them. Or at least point them to docs that do.

Comment thread src/http/fields.rs Outdated
wasi_fields
.append(key.as_str(), value.as_bytes())
.with_context(|| format!("wasi rejected header `{key}: {value:?}`"))?
if !FORBIDDEN_HEADERS.contains(key) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while technically HTTP headers are case sensitive, ones like Content-Length are not usually handled that way.

I recommend normalizing every entry in FORBIDDEN_HEADERS to be lowercase and also to use key.to_ascii_lowercase() here in this contains call as well.

@pchickey

pchickey commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Decided that this behavior is still too up-in-the-air to be implementable in wstd at this time - we need to continue spec conversations

@pchickey pchickey closed this Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants