/errors/http/err-invalid-char-invalid-character-in-header-content
TypeError [ERR_INVALID_CHAR]: Invalid character in header content
- 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
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.jsOutput
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:
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:
const auth = fs.readFileSync("token.txt", "utf8").trim();