h= is a trade. Every header you sign is a header nobody can tamper with, and a header whose alteration by a legitimate intermediary breaks your signature. Signing everything is as wrong as signing nothing.
The ones that must be signed
- From. Non-negotiable, and
Fromis the only header required by the specification. An unsigned From would let anyone change the displayed sender on a validly signed message, which is exactly the attack DKIM exists to stop. - Subject. The other field the reader acts on. Leaving it unsigned allows a signed message to be re-titled.
- To, Date, Message-ID. Cheap, stable, and they make replay and re-addressing visible.
The ones to leave out
- Received. Added by every hop. Signing it guarantees failure at the second one.
- Return-Path. Rewritten by the receiving server.
- List-*. If your mail ever reaches a mailing list, the list adds these. Signing them pre-emptively is a way of failing on lists.
- DKIM-Signature. Handled by the specification, not by you.
Oversigning
Listing a header name in h= more times than it appears in the message signs its absence as well as its value. It is the standard defence against a header being added rather than changed.
h=from:from:to:subject:date:message-id "From appears once in this message, and if a second From header shows up, this signature fails."
Without it, an attacker can prepend a second From header to a signed message. The signature still verifies against the original one, while many clients display the new one. Oversigning From and Subject closes that, costs nothing, and is what most modern signers do by default.
A reasonable default
h=from:from:to:subject:subject:date:message-id:mime-version:content-type — the fields a reader acts on, with the two most abusable oversigned, and nothing an intermediary routinely rewrites. If your signing platform does not let you set this, its own default is usually close enough; check it once rather than assuming.