Skip to content
WebhookKit Blog
Menu
← All articles

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:

ShellSingle quotes: 'X-Debug: shell-check'Double quotes: "X-Debug: shell-check"
BashWorks; keeps the contents literalWorks; variables such as $DEMO can expand
cmd.exeDoes not group the spaced header; apostrophes remain literalGroups the header correctly
PowerShellWorks; keeps the contents literalWorks; 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.