<p><img class="alignnone size-full wp-image-4472" src="https://flowgenius.in/wp-content/uploads/2026/01/Child-10-Cluster-10.png" alt="" /></p>
<p style="text-align: center;">Step by Step Guide to solve n8n Custom Header Errors</p>
<p> </p>
<p> </p>
<hr />
<p> </p>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Who this is for</strong>: n8n workflow designers who receive “Missing or malformed header” errors from HTTP Request nodes and need a reliable, production‑ready fix. <strong>We cover this in detail in the</strong> <a href="https://flowgenius.in/n8n-api-integration-errors/">n8n API Integration Errors Guide.</a></p>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">Quick Diagnosis</h2>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Problem</strong> – HTTP Request nodes reject calls with <em>Missing or malformed header</em> errors.</p>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>30‑second fix</strong></p>
<ol style="margin-bottom: 2em; line-height: 1.9;">
<li>Open the <strong>HTTP Request</strong> node → <strong>Headers</strong> tab.</li>
<li>Add the required header name (e.g., <code>Authorization</code>).</li>
<li>Set its value with an <strong>Expression</strong> that guarantees a string, e.g. <code>{{ String($json["token"]).trim() }}</code>.</li>
<li>Save → Run the workflow.</li>
</ol>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Result</strong> – The request succeeds; the error disappears.</p>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">1. Why n8n Throws “Custom Header Error”</h2>
<p><strong>If you encounter any</strong> <a href="/n8n-oauth2-token-refresh-errors">n8n oauth2 token refresh errors</a><strong> resolve them before continuing with the setup.</strong></p>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #ddd; padding: 12px;">Symptom</th>
<th style="border: 1px solid #ddd; padding: 12px;">Typical Cause</th>
<th style="border: 1px solid #ddd; padding: 12px;">n8n Diagnostic Message</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #ddd; padding: 12px;">Missing Header</td>
<td style="border: 1px solid #ddd; padding: 12px;">Header key not defined or expression returns <code>null/undefined</code></td>
<td style="border: 1px solid #ddd; padding: 12px;">Error: Request failed – Missing required header: X‑My‑Header</td>
</tr>
<tr>
<td style="border: 1px solid #ddd; padding: 12px;">Malformed Header</td>
<td style="border: 1px solid #ddd; padding: 12px;">Value is not a plain string (object, array, number) or contains illegal characters (<code>\n</code>, <code>\r</code>)</td>
<td style="border: 1px solid #ddd; padding: 12px;">Error: Header value for X‑My‑Header is not a valid string</td>
</tr>
<tr>
<td style="border: 1px solid #ddd; padding: 12px;">Encoding Issue</td>
<td style="border: 1px solid #ddd; padding: 12px;">UTF‑8 characters not URL‑encoded when required</td>
<td style="border: 1px solid #ddd; padding: 12px;">Error: Header value contains unsupported characters</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;"><em>Explanation</em>: n8n validates each header <strong>before</strong> sending the request. If a header is absent or its value fails the internal <code>typeof value === "string"</code> check, the node aborts with the messages above.</p>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">2. Step‑by‑Step Diagnosis</h2>
<p><strong>If you encounter any</strong> <a href="/n8n-credential-rotation-errors">n8n credential rotation errors</a><strong> resolve them before continuing with the setup.</strong></p>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Micro‑summary</strong> – Use the built‑in debugger to see exactly what value n8n is trying to send.</p>
<ol style="margin-bottom: 2em; line-height: 1.9;">
<li><strong>Enable Debug Mode</strong><br />
Gear ⚙️ → <strong>Settings</strong> → <strong>Execution Mode → “Run Once (Debug)”</strong>.</li>
<li><strong>Inspect the Headers in the Execution Log</strong><br />
Expand the failing <strong>HTTP Request</strong> node.<br />
Red badges highlight offending headers; hover to view the raw value (<code>null</code>, <code>[object Object]</code>, etc.).</li>
<li><strong>Trace the Source</strong><br />
Identify the upstream node that supplies the header (e.g., <strong>Set</strong>, <strong>Function</strong>, <strong>Webhook</strong>).<br />
Verify that node’s output in the same log.</li>
<li><strong>Validate Types in the Expression Editor</strong><br />
Enter <code>{{$json["myValue"]}}</code> → the preview must be a plain string.<br />
If it shows <code>object</code>, wrap with <code>String()</code> or <code>JSON.stringify()</code>.</li>
</ol>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">3. Fixing Missing Headers</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.1 Add a Static Header</h3>
<p style="margin-bottom: 2em; line-height: 1.9;"><em>Context</em>: Use when the header value never changes (API key, static token).</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">{
"headerParametersUi": {
"parameter": [
{
"name": "X-Api-Key",
"value": "YOUR_STATIC_KEY"
}
]
}
}
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.2 Add a Dynamic Header from Workflow Data</h3>
<p style="margin-bottom: 2em; line-height: 1.9;"><em>Context</em>: Pull a token or other value from a previous node.</p>
<p style="margin-bottom: 2em; line-height: 1.9;">1. <strong>Set node – expose the token</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">{
"values": [
{
"name": "token",
"value": "{{$json[\"access_token\"]}}"
}
]
}
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">2. <strong>Reference it in the HTTP Request node</strong> (Headers tab → <strong>Expression</strong>)</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">{{ $json["token"] }}
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">3. <strong>Force string conversion (EEFA)</strong> – protects against numbers, booleans, or <code>null</code>.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">{{ String($json["token"]).trim() }}
</pre>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">4. Fixing Malformed Headers</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #ddd; padding: 12px;">Issue</th>
<th style="border: 1px solid #ddd; padding: 12px;">Root Cause</th>
<th style="border: 1px solid #ddd; padding: 12px;">Remedy</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #ddd; padding: 12px;">Non‑string value</td>
<td style="border: 1px solid #ddd; padding: 12px;">Expression returns object/array</td>
<td style="border: 1px solid #ddd; padding: 12px;"><code>{{ JSON.stringify($json["payload"]) }}</code> or <code>{{ String($json["value"]) }}</code></td>
</tr>
<tr>
<td style="border: 1px solid #ddd; padding: 12px;">Trailing newline (<code>\n</code>/<code>\r</code>)</td>
<td style="border: 1px solid #ddd; padding: 12px;">Copied from a file or env var with line break</td>
<td style="border: 1px solid #ddd; padding: 12px;"><code>{{ $json["rawToken"].trim() }}</code></td>
</tr>
<tr>
<td style="border: 1px solid #ddd; padding: 12px;">Invalid characters (spaces, quotes)</td>
<td style="border: 1px solid #ddd; padding: 12px;">Token contains whitespace</td>
<td style="border: 1px solid #ddd; padding: 12px;"><code>{{ encodeURIComponent($json["token"]) }}</code></td>
</tr>
<tr>
<td style="border: 1px solid #ddd; padding: 12px;">Binary data (Buffer)</td>
<td style="border: 1px solid #ddd; padding: 12px;">Directly passing a Buffer object</td>
<td style="border: 1px solid #ddd; padding: 12px;"><code>{{ $json["file"].toString('base64') }}</code></td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Example: Clean Up a Token with Whitespace</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">{{ $json["rawToken"].trim() }}
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Example: Encode a URL‑Sensitive Header</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">{{ encodeURIComponent($json["callbackUrl"]) }}
</pre>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">5. Advanced: Dynamic Header Generation with Expressions</h2>
<p style="margin-bottom: 2em; line-height: 1.9;"><em>Context</em>: Build composite headers such as <code>Bearer <token></code> in a single expression.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">{{ `Bearer ${String($json["accessToken"]).trim()}` }}
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Why it works</strong></p>
<ul style="margin-bottom: 2em; line-height: 1.9;">
<li>Template literal (<code>` `</code>) guarantees a string result.</li>
<li><code>String()</code> forces the token to a string type, eliminating type‑mismatch errors.</li>
<li><code>.trim()</code> removes hidden newline characters that often trigger “malformed header” failures.</li>
</ul>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">6. Testing Outside n8n (Postman / curl)</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #ddd; padding: 12px;">Tool</th>
<th style="border: 1px solid #ddd; padding: 12px;">Command</th>
<th style="border: 1px solid #ddd; padding: 12px;">Expected Result</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #ddd; padding: 12px;">curl</td>
<td style="border: 1px solid #ddd; padding: 12px;">curl -H “X-Api-Key: $API_KEY” https://api.example.com/v1/resource</td>
<td style="border: 1px solid #ddd; padding: 12px;">200 OK if header is correct</td>
</tr>
<tr>
<td style="border: 1px solid #ddd; padding: 12px;">Postman</td>
<td style="border: 1px solid #ddd; padding: 12px;">Add header → <strong>Key</strong>: <code>X-Api-Key</code>, <strong>Value</strong>: <code>{{apiKey}}</code> (environment variable)</td>
<td style="border: 1px solid #ddd; padding: 12px;">Same as curl</td>
</tr>
<tr>
<td style="border: 1px solid #ddd; padding: 12px;">n8n</td>
<td style="border: 1px solid #ddd; padding: 12px;">Run workflow in <strong>Debug</strong> mode</td>
<td style="border: 1px solid #ddd; padding: 12px;">No “Custom Header Error” in log</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;">*If external tools succeed but n8n still fails, the problem lies in the node’s data handling (type conversion, whitespace, etc.).*</p>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">7. Production‑Grade EEFA Checklist</h2>
<ul style="margin-bottom: 2em; line-height: 1.9;">
<li><strong>Never store secrets in plain text</strong> – use n8n <strong>Credentials</strong> (<code>{{ $credentials.apiKey }}</code>) and reference them securely.</li>
<li><strong>Mask sensitive headers</strong> in logs: enable “Hide Sensitive Data” in workflow settings.</li>
<li><strong>Rate‑limit awareness</strong> – malformed‑header rejections can trigger IP throttling; verify header correctness before scaling.</li>
<li><strong>Retry logic</strong> – add a <strong>Retry</strong> node or enable “Continue On Fail” with exponential back‑off to prevent cascade failures.</li>
<li><strong>Audit header length</strong> – HTTP spec limits a header line to 8 KB; oversized custom headers may be silently dropped.</li>
</ul>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">8. Frequently Asked Questions</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">Q1. My header value must be a JSON object. Why does n8n still complain?</h3>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Answer</strong>: n8n expects a <strong>string</strong>. Convert the object: <code>{{ JSON.stringify($json["payload"]) }}</code>.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">Q2. Can I set headers globally for all HTTP Request nodes?</h3>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Answer</strong>: Not directly. Use <strong>Workflow Settings → “Default Request Headers”</strong> (available from v0.225) or create a reusable “Header Builder” sub‑workflow that returns a header map.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">Q3. The API says the header is optional, but n8n throws an error when I omit it.</h3>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Answer</strong>: Some APIs still require a placeholder. Add an empty string to satisfy n8n’s validation: <code>{{ "" }}</code>.</p>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">Conclusion</h2>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Key takeaways</strong></p>
<ol style="margin-bottom: 2em; line-height: 1.9;">
<li>n8n validates every header as a <em>plain string</em> before sending the request.</li>
<li>The most common failure modes are missing keys, non‑string values, and stray whitespace or illegal characters.</li>
<li>Resolve them by:
<ul style="margin-bottom: 1.5em; line-height: 1.9;">
<li>Defining the header explicitly in the node UI.</li>
<li>Using concise expressions that enforce <code>String()</code> conversion and <code>.trim()</code>.</li>
<li>Applying <code>encodeURIComponent()</code> or <code>JSON.stringify()</code> when the API expects encoded or JSON payloads.</li>
</ul>
</li>
<li>Verify the fix with n8n’s debug mode and, if needed, with external tools (curl/Postman).</li>
</ol>
<p style="margin-bottom: 2em; line-height: 1.9;">By following the EEFA checklist and the patterns above, your HTTP Request nodes will send well‑formed headers reliably, keeping your production workflows stable and secure.</p>

Step by Step Guide to solve n8n Custom Header Errors
Who this is for: n8n workflow designers who receive “Missing or malformed header” errors from HTTP Request nodes and need a reliable, production‑ready fix. We cover this in detail in the n8n API Integration Errors Guide.
Quick Diagnosis
Problem – HTTP Request nodes reject calls with Missing or malformed header errors.
30‑second fix
- Open the HTTP Request node → Headers tab.
- Add the required header name (e.g.,
Authorization).
- Set its value with an Expression that guarantees a string, e.g.
{{ String($json["token"]).trim() }}.
- Save → Run the workflow.
Result – The request succeeds; the error disappears.
1. Why n8n Throws “Custom Header Error”
If you encounter any n8n oauth2 token refresh errors resolve them before continuing with the setup.
| Symptom |
Typical Cause |
n8n Diagnostic Message |
| Missing Header |
Header key not defined or expression returns null/undefined |
Error: Request failed – Missing required header: X‑My‑Header |
| Malformed Header |
Value is not a plain string (object, array, number) or contains illegal characters (\n, \r) |
Error: Header value for X‑My‑Header is not a valid string |
| Encoding Issue |
UTF‑8 characters not URL‑encoded when required |
Error: Header value contains unsupported characters |
Explanation: n8n validates each header before sending the request. If a header is absent or its value fails the internal typeof value === "string" check, the node aborts with the messages above.
2. Step‑by‑Step Diagnosis
If you encounter any n8n credential rotation errors resolve them before continuing with the setup.
Micro‑summary – Use the built‑in debugger to see exactly what value n8n is trying to send.
- Enable Debug Mode
Gear ⚙️ → Settings → Execution Mode → “Run Once (Debug)”.
- Inspect the Headers in the Execution Log
Expand the failing HTTP Request node.
Red badges highlight offending headers; hover to view the raw value (null, [object Object], etc.).
- Trace the Source
Identify the upstream node that supplies the header (e.g., Set, Function, Webhook).
Verify that node’s output in the same log.
- Validate Types in the Expression Editor
Enter {{$json["myValue"]}} → the preview must be a plain string.
If it shows object, wrap with String() or JSON.stringify().
3. Fixing Missing Headers
3.1 Add a Static Header
Context: Use when the header value never changes (API key, static token).
{
"headerParametersUi": {
"parameter": [
{
"name": "X-Api-Key",
"value": "YOUR_STATIC_KEY"
}
]
}
}
3.2 Add a Dynamic Header from Workflow Data
Context: Pull a token or other value from a previous node.
1. Set node – expose the token
{
"values": [
{
"name": "token",
"value": "{{$json[\"access_token\"]}}"
}
]
}
2. Reference it in the HTTP Request node (Headers tab → Expression)
{{ $json["token"] }}
3. Force string conversion (EEFA) – protects against numbers, booleans, or null.
{{ String($json["token"]).trim() }}
4. Fixing Malformed Headers
| Issue |
Root Cause |
Remedy |
| Non‑string value |
Expression returns object/array |
{{ JSON.stringify($json["payload"]) }} or {{ String($json["value"]) }} |
Trailing newline (\n/\r) |
Copied from a file or env var with line break |
{{ $json["rawToken"].trim() }} |
| Invalid characters (spaces, quotes) |
Token contains whitespace |
{{ encodeURIComponent($json["token"]) }} |
| Binary data (Buffer) |
Directly passing a Buffer object |
{{ $json["file"].toString('base64') }} |
Example: Clean Up a Token with Whitespace
{{ $json["rawToken"].trim() }}
Example: Encode a URL‑Sensitive Header
{{ encodeURIComponent($json["callbackUrl"]) }}
5. Advanced: Dynamic Header Generation with Expressions
Context: Build composite headers such as Bearer <token> in a single expression.
{{ `Bearer ${String($json["accessToken"]).trim()}` }}
Why it works
- Template literal (
` `) guarantees a string result.
String() forces the token to a string type, eliminating type‑mismatch errors.
.trim() removes hidden newline characters that often trigger “malformed header” failures.
6. Testing Outside n8n (Postman / curl)
| Tool |
Command |
Expected Result |
| curl |
curl -H “X-Api-Key: $API_KEY” https://api.example.com/v1/resource |
200 OK if header is correct |
| Postman |
Add header → Key: X-Api-Key, Value: {{apiKey}} (environment variable) |
Same as curl |
| n8n |
Run workflow in Debug mode |
No “Custom Header Error” in log |
*If external tools succeed but n8n still fails, the problem lies in the node’s data handling (type conversion, whitespace, etc.).*
7. Production‑Grade EEFA Checklist
- Never store secrets in plain text – use n8n Credentials (
{{ $credentials.apiKey }}) and reference them securely.
- Mask sensitive headers in logs: enable “Hide Sensitive Data” in workflow settings.
- Rate‑limit awareness – malformed‑header rejections can trigger IP throttling; verify header correctness before scaling.
- Retry logic – add a Retry node or enable “Continue On Fail” with exponential back‑off to prevent cascade failures.
- Audit header length – HTTP spec limits a header line to 8 KB; oversized custom headers may be silently dropped.
8. Frequently Asked Questions
Q1. My header value must be a JSON object. Why does n8n still complain?
Answer: n8n expects a string. Convert the object: {{ JSON.stringify($json["payload"]) }}.
Q2. Can I set headers globally for all HTTP Request nodes?
Answer: Not directly. Use Workflow Settings → “Default Request Headers” (available from v0.225) or create a reusable “Header Builder” sub‑workflow that returns a header map.
Q3. The API says the header is optional, but n8n throws an error when I omit it.
Answer: Some APIs still require a placeholder. Add an empty string to satisfy n8n’s validation: {{ "" }}.
Conclusion
Key takeaways
- n8n validates every header as a plain string before sending the request.
- The most common failure modes are missing keys, non‑string values, and stray whitespace or illegal characters.
- Resolve them by:
- Defining the header explicitly in the node UI.
- Using concise expressions that enforce
String() conversion and .trim().
- Applying
encodeURIComponent() or JSON.stringify() when the API expects encoded or JSON payloads.
- Verify the fix with n8n’s debug mode and, if needed, with external tools (curl/Postman).
By following the EEFA checklist and the patterns above, your HTTP Request nodes will send well‑formed headers reliably, keeping your production workflows stable and secure.