<p dir="auto">Hello!</p>
<p dir="auto">This is pretty specific, but I was trying to use Deno as a test runner to test a web server that serves different content based on the <code class="notranslate">Host</code> header (NGINX server blocks / Apache VHosts), as Deno's batteries-included makes it very convenient.<br>
However, currently my testing code doesn't work using the built-in <code class="notranslate">fetch</code> nor when using the <code class="notranslate">node:http</code> compat module's <code class="notranslate">request()</code> (or any library that depends on it, like <code class="notranslate">npm:got</code>), because they silently ignore the <code class="notranslate">host</code> header. Trying to do the same thing in Node works.<br>
It makes sense that <code class="notranslate">fetch</code> should by default ignore the Host header like browsers do. However there should be an option to allow it as mentioned in the issues linked below, and the <code class="notranslate">node:http</code> module compat should allow it.</p>
<p dir="auto">I tried to do it myself, but I don't know Rust and got overwhelmed. For the record, this is where the Host (and Content Length) header is skipped:<br>
</p><div class="Box Box--condensed my-2">
  <div class="Box-header f6">
    <p class="mb-0 text-bold">
      <a href="https://github.com/denoland/deno/blob/92ba46c384806af171f1ad9c0040146a50531e84/ext/fetch/lib.rs#L307-L309">deno/ext/fetch/lib.rs</a>
    </p>
    <p class="mb-0 color-fg-muted">
        Lines 307 to 309
      in
      <a data-pjax="true" class="commit-tease-sha" href="/denoland/deno/commit/92ba46c384806af171f1ad9c0040146a50531e84">92ba46c</a>
    </p>
  </div>
  <div itemprop="text" class="Box-body p-0 blob-wrapper blob-wrapper-embedded data">
    <table class="highlight tab-size mb-0 js-file-line-container" data-tab-size="8" data-paste-markdown-skip="">

        <tbody><tr class="border-0">
          <td id="L307" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="307"></td>
          <td id="LC307" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line"> <span class="pl-k">if</span> !<span class="pl-en">matches</span><span class="pl-en">!</span><span class="pl-kos">(</span>name, <span class="pl-v">HOST</span> | <span class="pl-v">CONTENT_LENGTH</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> </td>
        </tr>

        <tr class="border-0">
          <td id="L308" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="308"></td>
          <td id="LC308" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line">   header_map<span class="pl-kos">.</span><span class="pl-en">append</span><span class="pl-kos">(</span>name<span class="pl-kos">,</span> v<span class="pl-kos">)</span><span class="pl-kos">;</span> </td>
        </tr>

        <tr class="border-0">
          <td id="L309" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="309"></td>
          <td id="LC309" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line"> <span class="pl-kos">}</span> </td>
        </tr>
    </tbody></table>
  </div>
</div>
<p></p>
<h4 dir="auto">Relevant issues / previous discussions:</h4>
<ul dir="auto">
<li><a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="665242054" data-permission-text="Title is private" data-url="https://github.com/denoland/deno/issues/6868" data-hovercard-type="issue" data-hovercard-url="/denoland/deno/issues/6868/hovercard" href="https://github.com/denoland/deno/issues/6868">#6868</a> (Jul 24, 2020) proposes an <code class="notranslate">allowAllHeaders</code> option to <code class="notranslate">Deno.createHttpClient</code><br>
As noted in the comments:
<blockquote>
<p dir="auto">The problem is <code class="notranslate">fetch()</code> will always behave differently in a server context and a client context. You have all the situations with same origin and cross origin which just don't make sense in a server context. I am still just for documenting it. As you state, Node's implementation just passes forbidden as well.</p>
</blockquote>
</li>
<li><a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="690171618" data-permission-text="Title is private" data-url="https://github.com/denoland/deno/issues/7307" data-hovercard-type="issue" data-hovercard-url="/denoland/deno/issues/7307/hovercard" href="https://github.com/denoland/deno/issues/7307">#7307</a> (Sep 1, 2020) talks about adding non-standard features to <code class="notranslate">fetch</code> via <code class="notranslate">HttpClient</code>.</li>
<li><a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="923856528" data-permission-text="Title is private" data-url="https://github.com/denoland/deno/issues/11017" data-hovercard-type="issue" data-hovercard-url="/denoland/deno/issues/11017/hovercard" href="https://github.com/denoland/deno/issues/11017">#11017</a> (Jun 17, 2021) claims that the "Host" header should always be set by reqwest / hyper to align with browser <code class="notranslate">fetch</code>.</li>
<li><a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="1276044087" data-permission-text="Title is private" data-url="https://github.com/denoland/deno/issues/14909" data-hovercard-type="issue" data-hovercard-url="/denoland/deno/issues/14909/hovercard" href="https://github.com/denoland/deno/issues/14909">#14909</a> (Jun 19, 2022) reports the lack of a way to set the "Host" header. Closed and marked as duplicate of <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="923856528" data-permission-text="Title is private" data-url="https://github.com/denoland/deno/issues/11017" data-hovercard-type="issue" data-hovercard-url="/denoland/deno/issues/11017/hovercard" href="https://github.com/denoland/deno/issues/11017">#11017</a>.</li>
<li><a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="1276052463" data-permission-text="Title is private" data-url="https://github.com/denoland/deno/issues/14910" data-hovercard-type="issue" data-hovercard-url="/denoland/deno/issues/14910/hovercard" href="https://github.com/denoland/deno/issues/14910">#14910</a> (Jun 19, 2022) by the same person about the Websocket API, closed for the same reason.</li>
<li><a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="1465267860" data-permission-text="Title is private" data-url="https://github.com/denoland/deno/issues/16840" data-hovercard-type="issue" data-hovercard-url="/denoland/deno/issues/16840/hovercard" href="https://github.com/denoland/deno/issues/16840">#16840</a> (Nov 26, 2022) asks for the <code class="notranslate">allowAllHeaders</code> proposed in the first issue.</li>
</ul>
<h4 dir="auto">Tests</h4>
<p dir="auto">Deno-only repro (from masx200 in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="1276044087" data-permission-text="Title is private" data-url="https://github.com/denoland/deno/issues/14909" data-hovercard-type="issue" data-hovercard-url="/denoland/deno/issues/14909/hovercard" href="https://github.com/denoland/deno/issues/14909">#14909</a>):</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import { serve } from &quot;https://deno.land/std@0.144.0/http/mod.ts&quot;;
import { assertEquals } from &quot;https://deno.land/std@0.144.0/testing/asserts.ts&quot;;

Deno.test(&quot;http-request-host&quot;, async () =&gt; {
    const ac = new AbortController();
    const { signal } = ac;
    const p = serve(async (req) =&gt; new Response(req.url), {
        signal,
        port: 5000,
    });
    const response = await fetch(&quot;http://127.0.0.1:5000/&quot;, {
        headers: { host: &quot;localhost.hello.world:9900&quot; },
    });
    const text = await response.text();
    assertEquals(text, &quot;http://localhost.hello.world:9900/&quot;);
    ac.abort();
    await p;
});"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">serve</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">"https://deno.land/std@0.144.0/http/mod.ts"</span><span class="pl-kos">;</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">assertEquals</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">"https://deno.land/std@0.144.0/testing/asserts.ts"</span><span class="pl-kos">;</span>

<span class="pl-v">Deno</span><span class="pl-kos">.</span><span class="pl-en">test</span><span class="pl-kos">(</span><span class="pl-s">"http-request-host"</span><span class="pl-kos">,</span> <span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-k">const</span> <span class="pl-s1">ac</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">AbortController</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">const</span> <span class="pl-kos">{</span> signal <span class="pl-kos">}</span> <span class="pl-c1">=</span> <span class="pl-s1">ac</span><span class="pl-kos">;</span>
    <span class="pl-k">const</span> <span class="pl-s1">p</span> <span class="pl-c1">=</span> <span class="pl-en">serve</span><span class="pl-kos">(</span><span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-s1">req</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-k">new</span> <span class="pl-v">Response</span><span class="pl-kos">(</span><span class="pl-s1">req</span><span class="pl-kos">.</span><span class="pl-c1">url</span><span class="pl-kos">)</span><span class="pl-kos">,</span> <span class="pl-kos">{</span>
        signal<span class="pl-kos">,</span>
        <span class="pl-c1">port</span>: <span class="pl-c1">5000</span><span class="pl-kos">,</span>
    <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">const</span> <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-en">fetch</span><span class="pl-kos">(</span><span class="pl-s">"http://127.0.0.1:5000/"</span><span class="pl-kos">,</span> <span class="pl-kos">{</span>
        <span class="pl-c1">headers</span>: <span class="pl-kos">{</span> <span class="pl-c1">host</span>: <span class="pl-s">"localhost.hello.world:9900"</span> <span class="pl-kos">}</span><span class="pl-kos">,</span>
    <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">const</span> <span class="pl-s1">text</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-s1">response</span><span class="pl-kos">.</span><span class="pl-en">text</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-en">assertEquals</span><span class="pl-kos">(</span><span class="pl-s1">text</span><span class="pl-kos">,</span> <span class="pl-s">"http://localhost.hello.world:9900/"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-s1">ac</span><span class="pl-kos">.</span><span class="pl-en">abort</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">await</span> <span class="pl-s1">p</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">Deno &amp; Node repro (my own code, under the Unlicense (feel free to reuse, re-license etc) if that matters). Run it with <code class="notranslate">deno run --allow-env=HOST,PORT --allow-net=localhost:8007 main.mjs</code> and <code class="notranslate">node main.mjs</code>.</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import assert from 'node:assert/strict';
import http from &quot;node:http&quot;;


const isDeno = typeof Deno !== &quot;undefined&quot;;
const host = (isDeno ? Deno.env.get(&quot;HOST&quot;) : process.env.HOST) || &quot;localhost&quot;;
const port = (isDeno ? Deno.env.get(&quot;PORT&quot;) : process.env.PORT) || 8007;

// Set up a server that replies with the received Host header
const server = http.createServer((req, res) =&gt; {
	res.writeHead(200);
	res.end(`Host header: &quot;${req.headers.host}&quot;`);
});
server.listen(port, host, () =&gt; {
    console.log(`Server is running on http://${host}:${port}`);
});


const CustomHostHeader = &quot;test.localhost&quot;

// Query our server
http.get(
	{ host, port, headers: { &quot;Host&quot;: CustomHostHeader } },
	(response) =&gt; {
		// Read the body...
		let body = &quot;&quot;;
		response.on(&quot;data&quot;, d =&gt; body += d);

		response.on(&quot;end&quot;, () =&gt; {
			console.log(&quot;body received:&quot;, body);

			try {
				assert.equal(body, `Host header: &quot;${CustomHostHeader}&quot;`);
				console.log(&quot;works!&quot;);
			} catch(e) {
				console.log(&quot;assertion error (deno):&quot;, e)
			}
			finally {
				server.close();
			}
		});
	}
);"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">assert</span> <span class="pl-k">from</span> <span class="pl-s">'node:assert/strict'</span><span class="pl-kos">;</span>
<span class="pl-k">import</span> <span class="pl-s1">http</span> <span class="pl-k">from</span> <span class="pl-s">"node:http"</span><span class="pl-kos">;</span>


<span class="pl-k">const</span> <span class="pl-s1">isDeno</span> <span class="pl-c1">=</span> <span class="pl-k">typeof</span> <span class="pl-v">Deno</span> <span class="pl-c1">!==</span> <span class="pl-s">"undefined"</span><span class="pl-kos">;</span>
<span class="pl-k">const</span> <span class="pl-s1">host</span> <span class="pl-c1">=</span> <span class="pl-kos">(</span><span class="pl-s1">isDeno</span> ? <span class="pl-v">Deno</span><span class="pl-kos">.</span><span class="pl-c1">env</span><span class="pl-kos">.</span><span class="pl-en">get</span><span class="pl-kos">(</span><span class="pl-s">"HOST"</span><span class="pl-kos">)</span> : <span class="pl-s1">process</span><span class="pl-kos">.</span><span class="pl-c1">env</span><span class="pl-kos">.</span><span class="pl-c1">HOST</span><span class="pl-kos">)</span> <span class="pl-c1">||</span> <span class="pl-s">"localhost"</span><span class="pl-kos">;</span>
<span class="pl-k">const</span> <span class="pl-s1">port</span> <span class="pl-c1">=</span> <span class="pl-kos">(</span><span class="pl-s1">isDeno</span> ? <span class="pl-v">Deno</span><span class="pl-kos">.</span><span class="pl-c1">env</span><span class="pl-kos">.</span><span class="pl-en">get</span><span class="pl-kos">(</span><span class="pl-s">"PORT"</span><span class="pl-kos">)</span> : <span class="pl-s1">process</span><span class="pl-kos">.</span><span class="pl-c1">env</span><span class="pl-kos">.</span><span class="pl-c1">PORT</span><span class="pl-kos">)</span> <span class="pl-c1">||</span> <span class="pl-c1">8007</span><span class="pl-kos">;</span>

<span class="pl-c">// Set up a server that replies with the received Host header</span>
<span class="pl-k">const</span> <span class="pl-s1">server</span> <span class="pl-c1">=</span> <span class="pl-s1">http</span><span class="pl-kos">.</span><span class="pl-en">createServer</span><span class="pl-kos">(</span><span class="pl-kos">(</span><span class="pl-s1">req</span><span class="pl-kos">,</span> <span class="pl-s1">res</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
	<span class="pl-s1">res</span><span class="pl-kos">.</span><span class="pl-en">writeHead</span><span class="pl-kos">(</span><span class="pl-c1">200</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
	<span class="pl-s1">res</span><span class="pl-kos">.</span><span class="pl-en">end</span><span class="pl-kos">(</span><span class="pl-s">`Host header: "<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">req</span><span class="pl-kos">.</span><span class="pl-c1">headers</span><span class="pl-kos">.</span><span class="pl-c1">host</span><span class="pl-kos">}</span></span>"`</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-s1">server</span><span class="pl-kos">.</span><span class="pl-en">listen</span><span class="pl-kos">(</span><span class="pl-s1">port</span><span class="pl-kos">,</span> <span class="pl-s1">host</span><span class="pl-kos">,</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s">`Server is running on http://<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">host</span><span class="pl-kos">}</span></span>:<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">port</span><span class="pl-kos">}</span></span>`</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>


<span class="pl-k">const</span> <span class="pl-v">CustomHostHeader</span> <span class="pl-c1">=</span> <span class="pl-s">"test.localhost"</span>

<span class="pl-c">// Query our server</span>
<span class="pl-s1">http</span><span class="pl-kos">.</span><span class="pl-en">get</span><span class="pl-kos">(</span>
	<span class="pl-kos">{</span> host<span class="pl-kos">,</span> port<span class="pl-kos">,</span> <span class="pl-c1">headers</span>: <span class="pl-kos">{</span> <span class="pl-s">"Host"</span>: <span class="pl-v">CustomHostHeader</span> <span class="pl-kos">}</span> <span class="pl-kos">}</span><span class="pl-kos">,</span>
	<span class="pl-kos">(</span><span class="pl-s1">response</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
		<span class="pl-c">// Read the body...</span>
		<span class="pl-k">let</span> <span class="pl-s1">body</span> <span class="pl-c1">=</span> <span class="pl-s">""</span><span class="pl-kos">;</span>
		<span class="pl-s1">response</span><span class="pl-kos">.</span><span class="pl-en">on</span><span class="pl-kos">(</span><span class="pl-s">"data"</span><span class="pl-kos">,</span> <span class="pl-s1">d</span> <span class="pl-c1">=&gt;</span> <span class="pl-s1">body</span> <span class="pl-c1">+=</span> <span class="pl-s1">d</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

		<span class="pl-s1">response</span><span class="pl-kos">.</span><span class="pl-en">on</span><span class="pl-kos">(</span><span class="pl-s">"end"</span><span class="pl-kos">,</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
			<span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s">"body received:"</span><span class="pl-kos">,</span> <span class="pl-s1">body</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

			<span class="pl-k">try</span> <span class="pl-kos">{</span>
				<span class="pl-s1">assert</span><span class="pl-kos">.</span><span class="pl-en">equal</span><span class="pl-kos">(</span><span class="pl-s1">body</span><span class="pl-kos">,</span> <span class="pl-s">`Host header: "<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-v">CustomHostHeader</span><span class="pl-kos">}</span></span>"`</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
				<span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s">"works!"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
			<span class="pl-kos">}</span> <span class="pl-k">catch</span><span class="pl-kos">(</span><span class="pl-s1">e</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
				<span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s">"assertion error (deno):"</span><span class="pl-kos">,</span> <span class="pl-s1">e</span><span class="pl-kos">)</span>
			<span class="pl-kos">}</span>
			<span class="pl-k">finally</span> <span class="pl-kos">{</span>
				<span class="pl-s1">server</span><span class="pl-kos">.</span><span class="pl-en">close</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
			<span class="pl-kos">}</span>
		<span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
	<span class="pl-kos">}</span>
<span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">Thanks for your time!</p>
<p dir="auto">Only happening occasionally - only observed on windows.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="test fetchPostBodyTypedArray_permW0N1E0R0
HttpUser: connection closed
    at maybeError (deno/js/errors.ts:38:12)
    at handleAsyncMsgFromRust (deno/js/dispatch.ts:27:17)
test result: FAILED. 7 passed; 1 failed; 0 ignored; 0 measured; 206 filtered out
Error: There were 1 test failures.
    at setTimeout (file:///C:/deno/js/testing/testing.ts:110:13)
    at fire (deno/js/timers.ts:131:3)"><pre class="notranslate"><code class="notranslate">test fetchPostBodyTypedArray_permW0N1E0R0
HttpUser: connection closed
    at maybeError (deno/js/errors.ts:38:12)
    at handleAsyncMsgFromRust (deno/js/dispatch.ts:27:17)
test result: FAILED. 7 passed; 1 failed; 0 ignored; 0 measured; 206 filtered out
Error: There were 1 test failures.
    at setTimeout (file:///C:/deno/js/testing/testing.ts:110:13)
    at fire (deno/js/timers.ts:131:3)
</code></pre></div>
<p dir="auto">ex: <a href="https://ci.appveyor.com/project/deno/deno/builds/20541747" rel="nofollow">https://ci.appveyor.com/project/deno/deno/builds/20541747</a></p>