Enviar correos desde PHP parece sencillo al principio. La función mail() existe desde la primera versión del lenguaje. Pero en producción, mail() sola es insuficiente: los correos acaban en spam, no soporta SMTP autenticado, el HTML queda limitado y los adjuntos son un dolor. PHPMailer cubre todo eso sin añadir complejidad innecesaria.
La función mail() nativa y sus limitaciones
<?php
// Uso básico de mail()
$para = '[email protected]';
$asunto = 'Prueba de email';
$mensaje = 'Este es el cuerpo del mensaje.';
$cabeceras = 'From: [email protected]' . "rn" .
'Reply-To: [email protected]' . "rn" .
'Content-Type: text/plain; charset=UTF-8';
$enviado = mail($para, $asunto, $mensaje, $cabeceras);
echo $enviado ? 'Enviado' : 'Error al enviar';
Los problemas de mail(): depende del MTA del servidor (sendmail, postfix), no soporta SMTP con credenciales, no tiene reintentos, los headers hay que escribirlos a mano y es difícil de depurar. Para proyectos reales, usa PHPMailer.
Instalar PHPMailer
composer require phpmailer/phpmailer
Enviar con SMTP de Gmail
<?php
use PHPMailerPHPMailerPHPMailer;
use PHPMailerPHPMailerSMTP;
use PHPMailerPHPMailerException;
require 'vendor/autoload.php';
$mail = new PHPMailer(true); // true activa excepciones
try {
// Configuración SMTP
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->SMTPAuth = true;
$mail->Username = '[email protected]';
$mail->Password = 'app_password_aqui'; // contraseña de aplicación, no la de Gmail
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->CharSet = 'UTF-8';
// Remitente y destinatario
$mail->setFrom('[email protected]', 'Mi Aplicación');
$mail->addAddress('[email protected]', 'Ana García');
$mail->addCC('[email protected]');
$mail->addBCC('[email protected]');
// Contenido
$mail->isHTML(true);
$mail->Subject = 'Confirmación de pedido #12345';
$mail->Body = '<h1>Pedido confirmado</h1><p>Hola Ana, tu pedido está en camino.</p>';
$mail->AltBody = 'Hola Ana, tu pedido está en camino.'; // texto plano alternativo
$mail->send();
echo 'Email enviado correctamente';
} catch (Exception $e) {
echo "Error: {$mail->ErrorInfo}";
}
Enviar con SendGrid (alternativa a Gmail para producción)
<?php
$mail->isSMTP();
$mail->Host = 'smtp.sendgrid.net';
$mail->SMTPAuth = true;
$mail->Username = 'apikey'; // literal: "apikey"
$mail->Password = 'SG.TU_CLAVE_API_AQUI';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
Email HTML con texto alternativo
Los clientes de email que no soportan HTML mostrarán el AltBody. Siempre debes proporcionarlo:
<?php
$mail->isHTML(true);
$mail->Subject = 'Tu factura de septiembre';
$mail->Body = <<<HTML
<!DOCTYPE html>
<html>
<body style="font-family: Arial, sans-serif; color: #333;">
<h2 style="color: #2563eb;">Factura #2026-09</h2>
<p>Hola <strong>Ana</strong>, adjuntamos tu factura de septiembre.</p>
<table border="0" cellpadding="8" style="border-collapse: collapse;">
<tr><td>Servicio:</td><td><strong>Plan Pro</strong></td></tr>
<tr><td>Importe:</td><td><strong>49,00 </strong></td></tr>
</table>
<p style="color: #666; font-size: 0.9em;">Este email fue enviado automáticamente.</p>
</body>
</html>
HTML;
$mail->AltBody = "Hola Ana, adjuntamos tu factura de septiembre.nPlan Pro: 49,00 ";
Adjuntos con addAttachment()
<?php
// Adjuntar un fichero del servidor
$mail->addAttachment('/var/facturas/factura_2026_09.pdf', 'Factura_Septiembre.pdf');
// Adjuntar desde string (sin escribir en disco)
$pdfContent = generarFacturaPDF($pedido);
$mail->addStringAttachment($pdfContent, 'Factura.pdf', PHPMailer::ENCODING_BASE64, 'application/pdf');
// Adjuntar imagen inline (para usarla en el Body con cid:)
$cid = $mail->addEmbeddedImage('/ruta/logo.png', 'logo_cid');
$mail->Body = '<img src="cid:logo_cid"><p>Contenido...</p>';
// Múltiples adjuntos
foreach ($archivos as $ruta => $nombre) {
$mail->addAttachment($ruta, $nombre);
}
Configurar From y ReplyTo correctamente
Una de las causas más frecuentes de que los emails caigan en spam es que el From no coincida con el dominio del servidor de envío:
<?php
// BIEN: From del mismo dominio que el servidor SMTP
$mail->setFrom('[email protected]', 'Mi Aplicación');
$mail->addReplyTo('[email protected]', 'Soporte');
// MAL: From de un dominio diferente al servidor SMTP
// Si envías desde smtp.gmail.com pero pones From: [email protected]
// Gmail lo reescribirá a [email protected]
// Para envíos desde dominio propio: usa un relay SMTP de tu proveedor
// o servicios como SendGrid, Postmark, Mailgun con SPF/DKIM configurados
Depuración y niveles de debug
<?php
// Activa la salida de debug (solo en desarrollo)
$mail->SMTPDebug = SMTP::DEBUG_SERVER; // muestra la conversación SMTP completa
// Niveles: DEBUG_OFF (0), DEBUG_CLIENT (1), DEBUG_SERVER (2), DEBUG_CONNECTION (3)
// Redirigir el debug a un log en lugar de pantalla
$mail->Debugoutput = function(string $str, int $level) {
file_put_contents('/var/log/phpmailer.log', date('Y-m-d H:i:s') . ' ' . $str . "n", FILE_APPEND);
};
Clase auxiliar para centralizar la configuración
<?php
class Mailer {
private PHPMailer $mail;
public function __construct() {
$this->mail = new PHPMailer(true);
$this->mail->isSMTP();
$this->mail->Host = $_ENV['SMTP_HOST'];
$this->mail->SMTPAuth = true;
$this->mail->Username = $_ENV['SMTP_USER'];
$this->mail->Password = $_ENV['SMTP_PASS'];
$this->mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$this->mail->Port = (int)$_ENV['SMTP_PORT'];
$this->mail->CharSet = 'UTF-8';
$this->mail->setFrom($_ENV['MAIL_FROM'], $_ENV['MAIL_FROM_NAME']);
}
public function enviar(string $para, string $nombre, string $asunto, string $html, string $texto = ''): void {
$this->mail->clearAddresses();
$this->mail->clearAttachments();
$this->mail->addAddress($para, $nombre);
$this->mail->isHTML(true);
$this->mail->Subject = $asunto;
$this->mail->Body = $html;
$this->mail->AltBody = $texto ?: strip_tags($html);
$this->mail->send();
}
}
$mailer = new Mailer();
$mailer->enviar('[email protected]', 'Ana García', 'Bienvenida', '<p>Hola Ana</p>');
Problemas comunes y soluciones
- Error «Could not authenticate»: Gmail requiere contraseñas de aplicación (no la contraseña de tu cuenta). Actívala en Configuración > Seguridad > Contraseñas de aplicación.
- Emails en spam: configura SPF, DKIM y DMARC en tu DNS. PHPMailer no puede solucionar problemas de reputación de dominio.
- Caracteres raros en el asunto: PHPMailer codifica automáticamente el Subject en UTF-8. No uses
=?UTF-8?manual. - Adjunto con nombre en español: PHPMailer lo codifica correctamente; no necesitas manipular el nombre manualmente.
- mail() no funciona en hosting compartido: muchos hostings la deshabilitan o limitan. Usa siempre SMTP explícito.
