Forcing a Content-Type header with Guzzle's form_params

Oh Dear! monitors your entire site, not just the homepage. We crawl and search for broken pages and mixed content, send alerts when your site is down and notify you on expiring SSL certificates.

Start your free 10 day trial! »

Image of Mattias Geniar

Mattias Geniar, March 09, 2020

Follow me on Twitter as @mattiasgeniar

I just lost an hour or 2 of my life to this, so I figure I’ll do a small write-up to save future me from having to do the same dance.

I had the following piece of code, that wasn’t doing what I expected.

<?php
$client = new Client([
    'http_errors' => false,
    'verify' => false,
    'headers' => [
        'Content-Type' => 'application/json',
    ],
]);

try {
    $response = $client->post('/', [
        'form_params' => '{"query":"{some_json}"}',
    ]);
} catch (GuzzleException $exception) {
    // ...
}

Whenever the Guzzle call would execute, it would overwrite my provided Content-Type header with the application/x-www-form-urlencoded value.

What I’m trying to do looks weird, but is intentional: I want to send a JSON payload, but send it as if it was a simple form submission. I don’t want to use Guzzle’s json method for this.

This should work, because the docs say:

[form_params] Sets the Content-Type header to application/x-www-form-urlencoded when no Content-Type header is already present.

But my header was already present, yet Guzzle overwrites it!

Turns out, I was applying that header in the wrong place. Silly me. The headers array should be moved directly to the $client->post() call, not set as the defaults of the client.

My fix was to just move them:

<?php
$client = new Client([
    'http_errors' => false,
    'verify' => false,
]);

try {
    $response = $client->post('/', [
        'headers' => [
            'Content-Type' => 'application/json',
        ],
        'form_params' => '{"query":"{some_json}"}',
    ]);
} catch (GuzzleException $exception) {
    // ...
}

Remove them from the Client() instantiation and set the headers directly to the HTTP call that is being executed.

Lesson learned: the docs might be right, but they aren’t very clear.



Want to subscribe to the cron.weekly newsletter?

I write a weekly-ish newsletter on Linux, open source & webdevelopment called cron.weekly.

It features the latest news, guides & tutorials and new open source projects. You can sign up via email below.

No spam. Just some good, practical Linux & open source content.