curl headers on Linux and Windows: check your shell quotes
Fix curl -H quoting in Bash, Windows Command Prompt and PowerShell, then use verbose output to check which request headers curl sends.
If a curl -H header works in Bash but fails in Windows Command Prompt, wrap the complete Name: value in double quotes. In PowerShell, single quotes also work for a simple literal header; use curl.exe to select the curl executable explicitly. The shell matters more than the operating system.
While debugging another application locally, I was convinced a Spring filter was removing my headers. The cause turned out to be shell quoting. Before changing a filter, check what curl actually sends.
Use the command for your shell
These examples send two harmless diagnostic headers. Replace http://localhost:8080/ with a route in your running application or a receiver URL you control; these commands do not start a server.
Bash, including Bash on Linux, WSL or Git Bash:
curl -v -H 'X-Debug: shell-check' -H 'X-Source: tutorial' http://localhost:8080/
Windows Command Prompt (cmd.exe):
curl.exe -v -H "X-Debug: shell-check" -H "X-Source: tutorial" http://localhost:8080/
Windows PowerShell 5.1 or PowerShell 7 on Windows:
curl.exe -v -H 'X-Debug: shell-check' -H 'X-Source: tutorial' http://localhost:8080/
Each -H takes one header argument. Repeat it for additional headers, keeping each complete header together. See curl's --header documentation.
For these simple headers, the quote rules are:
| Shell | Single quotes: 'X-Debug: shell-check' | Double quotes: "X-Debug: shell-check" |
|---|---|---|
| Bash | Works; keeps the contents literal | Works; variables such as $DEMO can expand |
| cmd.exe | Does not group the spaced header; apostrophes remain literal | Groups the header correctly |
| PowerShell | Works; keeps the contents literal | Works; variables such as $DEMO can expand |
These distinctions follow the Bash quoting rules, Windows argument parsing rules and PowerShell quoting rules. A Windows Terminal tab can host different shells, so “I'm on Windows” does not identify the right syntax.
In cmd.exe, copying the Bash example can leave literal apostrophes in arguments and split the header at the space. Depending on the command, this can produce a different header name, an extra argument treated as a URL, or an error. There is no universal “wrong quotes silently drop the header” behavior. The curl FAQ on quote failures explains why the command interpreter matters.
Check outgoing headers with curl -v
Look for the diagnostic headers in the verbose output. This excerpt was observed when running the Bash example against a temporary local receiver:
> X-Debug: shell-check
> X-Source: tutorial
With -v, > marks headers curl sends and < marks headers it receives. Compare the exact name and value: an unexpected apostrophe is a different character, not decoration.
Two nearby flags answer a different question: -I requests HTTP HEAD and displays response headers; -i includes response headers alongside the response body. Neither replaces checking outgoing headers with -v.
If the outgoing header is correct, compare it with what your receiver records. Verbose output shows curl's view of the request; it does not prove that every proxy or application layer received it unchanged. Redact credentials and sensitive values before sharing verbose logs.
Check the executable, expansion and line breaks
PowerShell's curl may be an alias. Windows PowerShell 5.1 defines curl as an alias for Invoke-WebRequest, which accepts different parameters. PowerShell 7+ does not define that default alias. Using curl.exe selects the executable without changing your aliases. See Microsoft's curl on Windows documentation.
In PowerShell, inspect command resolution and versions with:
Get-Command curl
$PSVersionTable.PSVersion
curl.exe --version
Double quotes can change a header value. In Bash, these illustrative commands send $DEMO and expanded, respectively:
DEMO=expanded
curl -v -H 'X-Debug: $DEMO' http://localhost:8080/
curl -v -H "X-Debug: $DEMO" http://localhost:8080/
Bash expands variables inside double quotes; single quotes preserve them. PowerShell has the same literal-versus-expandable distinction for these simple strings, though its variable assignment syntax differs. See Bash double quotes and PowerShell quoted strings. Changing every single quote to a double quote can therefore change the data you send.
Start with one line. Multiline continuation uses a backslash in Bash, a caret in cmd.exe and a backtick in PowerShell. Copying those characters between shells can break the command; trailing spaces can also break continuation. The Microsoft shell syntax comparison shows the differences.
Follow the header to the receiver
Confirm your shell and executable, send one harmless header, then compare its outgoing > line with the receiver's recorded value. That evidence tells you where to investigate next: argument handling if curl's header is wrong, or the request path and application layers if the receiver sees something different.
For an optional independent receiver, create a receiving URL in WebhookKit, send the diagnostic header there and inspect Headers. This shows what arrived at that receiver, not what reached your separate Spring application. Use synthetic values rather than real credentials with a hosted diagnostic service.
Inspect your next webhook with WebhookKit.