FormData で送るとテキストだけでも multipart/form-data 形式で送信されるようです
自分で Content-Type を指定しなくても自動で

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryEeFLLVciEeJfWzFZ

みたいなヘッダーが追加されます

また PHP で生の POST データを受け取るための php://input は multipart 非対応らしいです
https://www.php.net/manual/ja/wrappers.php.php

送信データにファイルが無い場合でも multipart なら受け取れません
そのせいで FormData で送ると php://input でデータを受け取れないということになります

以下確認用

↓のページを用意

<?php

var_dump($_POST);
var_dump(file_get_contents('php://input'));

● 普通の form としての POST を実行してみる

fetch("", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: "a=1" })

レスポンス:
array(1) { ["a"]=> string(1) "1" } string(3) "a=1"

⇨ $_POST と php://input の両方で受け取れてる

● FormData で POST してみる

const fd = new FormData()
fd.append("a", "1")
fetch("", { method: "POST", body: fd })

ペイロード:
------WebKitFormBoundary7KvCNMsEQGTuSHn7
Content-Disposition: form-data; name="a"

1
------WebKitFormBoundary7KvCNMsEQGTuSHn7--

レスポンス:
array(1) { ["a"]=> string(1) "1" } string(0) ""

⇨ $_POST でだけ受け取れてる