Skip to content

/errors/http/err-invalid-char-invalid-character-in-header-content

TypeError [ERR_INVALID_CHAR]: Invalid character in header content

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · curl 8.21.0 (x86_64-w64-mingw32) libcurl/8.21.0 Schannel zlib/1.3.2updated
tool
HTTP
exit code
1
versions
Node.js 18+
os
any

TypeError [ERR_INVALID_CHAR]: Invalid character in header content ["X-Name"] is thrown by Node.js setHeader() (and writeHead(), and the client headers option) when a header value contains a character HTTP does not allow there: a newline (CR or LF) or a character above Latin-1, such as ✓ or a Chinese file name. The header name in brackets tells you which one. Percent-encode the value, or trim the stray newline.

Reproduce it

bash
cat > headers.js <<'EOF'
const http = require("http");
const srv = http.createServer((req, res) => {
  for (const [name, value] of [
    ["X-Latin1", "résumé"],
    ["X-Name", "Ünïcode ✓"],
    ["X-User", "alice\r\nSet-Cookie: admin=1"],
  ]) {
    try {
      res.setHeader(name, value);
      console.log(name, "accepted");
    } catch (e) {
      console.log(`${e.name} [${e.code}]: ${e.message}`);
    }
  }
  res.setHeader("X-Safe", encodeURIComponent("Ünïcode ✓"));
  res.end();
});
srv.listen(4924, "127.0.0.1", () => {
  http.get("http://127.0.0.1:4924/", (r) => {
    console.log("X-Safe:", r.headers["x-safe"]);
    r.resume();
    srv.close();
  });
});
EOF
node headers.js

Output

X-Latin1 accepted
TypeError [ERR_INVALID_CHAR]: Invalid character in header content ["X-Name"]
TypeError [ERR_INVALID_CHAR]: Invalid character in header content ["X-User"]
X-Safe: %C3%9Cn%C3%AFcode%20%E2%9C%93

Latin-1 characters such as é pass the check, but the receiving side may decode them differently, so do not rely on them either. Rejecting CR and LF is what protects you from header injection (Set-Cookie smuggled in through a value).

Non-ASCII file names in downloads

Use the RFC 6266 / RFC 8187 form with an ASCII fallback:

js
const name = "Bericht für März.pdf";
res.setHeader(
  "Content-Disposition",
  `attachment; filename="report.pdf"; filename*=UTF-8''${encodeURIComponent(name)}`
);

In Express, res.attachment(name) and res.download(path, name) build this header for you.

Values from files and environment

A value read with fs.readFileSync() usually ends with \n; a token in an environment variable copied from a terminal may too. Trim it before using it as a header:

js
const auth = fs.readFileSync("token.txt", "utf8").trim();

Sources