Ukázky · 2026
Truhlárna Smrk
Ukázka opravy kontaktního formuláře v PHP. Správné hlavičky e-mailu, nastavení SPF, DKIM a DMARC a ochrana proti zneužití.
Shrnutí pro zájemce o web
Truhlárna Smrk má na webu kontaktní formulář. Návštěvník ho odešle, uvidí poděkování, a přesto část zpráv majiteli nikdy nedorazí. Poptávky tiše mizí a nikdo o tom neví. Na takové závadě je nejhorší právě to, že web vypadá, jako by fungoval.
Příčina byla ve způsobu, jakým formulář e-mail odesílal. Oprava spočívala v tom, že se formulář přestal vydávat za návštěvníka a doména dostala správné ověřovací záznamy. Zpráva teď dorazí do schránky a odpovědět zákazníkovi stačí jedním kliknutím.
Příčina
Formulář vkládal do hlavičky From adresu návštěvníka, například jan@gmail.com, ale zprávu fyzicky odesílal server hostingu. Přijímající server tak vidí zprávu, která tvrdí, že přichází z Gmailu, a odesílá ji cizí server bez oprávnění. Kontrola SPF, DKIM a DMARC selže a zpráva skončí ve spamu, nebo je rovnou odmítnuta.
Druhý problém byl v tom, že funkce mail() vrací true, jakmile zprávu převezme odesílací server. Neříká nic o tom, zda dorazila. Formulář proto hlásil úspěch i při nedoručení.
Oprava
- Odesílatel:
Fromje pevná adresa na vlastní doméně, adresa návštěvníka jde doReply-To. - Obálka zprávy: pátý parametr
mail()(-f) nastaví stejnou adresu i jako envelope sender, který kontroluje SPF. - SPF: TXT záznam domény povolí servery hostingu. Doména smí mít jen jeden SPF záznam, jinak se kontrola nevyhodnotí.
- DKIM: podpis zapnutý u hostingu a veřejný klíč v DNS pod příslušným selektorem.
- DMARC: záznam
_dmarczačíná nap=nones adresou pro reporty. Po několika týdnech sledování se zpřísní naquarantine. - Zabezpečení: zalomení řádků se z jména odstraní (ochrana proti vkládání hlaviček), e-mail se ověří přes
FILTER_VALIDATE_EMAIL, skryté pole zachytává roboty a předmět s diakritikou se kóduje podle RFC 2047.
$name = trim(str_replace(["\r", "\n"], ' ', $_POST['name'] ?? ''));
$email = filter_var($_POST['email'] ?? '', FILTER_VALIDATE_EMAIL);
if ($email === false || $name === '') {
http_response_code(422);
exit;
}
$subject = '=?UTF-8?B?' . base64_encode('Poptávka z webu') . '?=';
$headers = [
'From' => 'Formulář <formular@priklad.cz>',
'Reply-To' => $email,
'Content-Type' => 'text/plain; charset=UTF-8',
];
$sent = mail($to, $subject, $message, $headers, '-fformular@priklad.cz');
Pole hlaviček v mail() vyžaduje PHP 7.2 a novější.
Jak jsem ověřil, že to funguje
Zkušební zprávu jsem poslal na Gmail a v zobrazení původní zprávy zkontroloval, že SPF, DKIM i DMARC mají výsledek PASS. Totéž jsem zkusil na adresu u Seznamu. Když mail() selže, formulář nově zobrazí chybu s telefonním číslem. Každá odeslaná zpráva se navíc zapisuje do souboru mimo veřejný adresář s omezenou dobou uchování, takže poptávka nezmizí ani při výpadku e-mailu.
Proč je to v portfoliu
Jde o modelový případ, ale závada je jedna z nejčastějších, se kterými se u cizích webů setkávám. Ukazuje můj postup při opravách: nejdřív najít skutečnou příčinu, ne jen přidat další odesílací službu, a nakonec ověřit výsledek na reálných schránkách.