Updated 2026-10-08
How to Send Email with PHPMailer over SMTP
To send email with PHPMailer through an SMTP relay, call isSMTP() and set Host to smtp.bulko.io, SMTPAuth to true, Username and Password from environment variables, SMTPSecure to PHPMailer::ENCRYPTION_STARTTLS and Port to 587. For port 465 use PHPMailer::ENCRYPTION_SMTPS instead.
PHPMailer SMTP settings
| Field | Value |
|---|---|
| Host | smtp.bulko.io |
| SMTPAuth | true |
| Username | getenv('SMTP_USERNAME') (e.g. smtp_xxxxxxxxxxxxxxxx) |
| Password | getenv('SMTP_PASSWORD') |
| SMTPSecure | PHPMailer::ENCRYPTION_STARTTLS for 587/2525, PHPMailer::ENCRYPTION_SMTPS for 465 |
| Port | 587 (or 2525 if blocked, 465 with SMTPS) |
| setFrom() | An address on your domain, e.g. [email protected] |
Step-by-step setup
- Buy an SMTP package on the Bulko SMTP Relay page and copy the username and password from Dashboard -> SMTP Access.
- Install PHPMailer with
composer require phpmailer/phpmailerand loadvendor/autoload.php. - Expose the credentials as environment variables
SMTP_USERNAMEandSMTP_PASSWORD(web server, PHP-FPM pool config or your framework's .env loader) and read them withgetenv(). - Create
new PHPMailer(true)so errors throw exceptions, callisSMTP(), then setHost,SMTPAuth = true,UsernameandPassword. - Match encryption to the port.
PHPMailer::ENCRYPTION_STARTTLS(the stringtls) goes with 587 or 2525.PHPMailer::ENCRYPTION_SMTPS(the stringssl) goes with 465. Mixing them is the most common PHPMailer SMTP error. - Call
setFrom()with an address on your own domain. For aligned SPF, DKIM and DMARC, verify the domain under Sending Domains in the Bulko dashboard. - Set the content (
Subject,Body,AltBody) and callsend()insidetry, printing$mail->ErrorInfoon failure.
Shared hosting sometimes blocks outbound 587 and 465. In that case keep ENCRYPTION_STARTTLS and set Port = 2525. More detail in the SMTP ports guide.
send-test.php
<?php
// send-test.php (composer require phpmailer/phpmailer)
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require __DIR__ . '/vendor/autoload.php';
$mail = new PHPMailer(true); // true = throw exceptions on errors
try {
// $mail->SMTPDebug = SMTP::DEBUG_SERVER; // uncomment to print the SMTP dialogue
$mail->isSMTP();
$mail->Host = 'smtp.bulko.io';
$mail->SMTPAuth = true;
$mail->Username = getenv('SMTP_USERNAME');
$mail->Password = getenv('SMTP_PASSWORD');
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // 587 or 2525
$mail->Port = 587;
// Port 465 instead:
// $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
// $mail->Port = 465;
$mail->CharSet = PHPMailer::CHARSET_UTF8;
$mail->setFrom('[email protected]', 'Your App');
$mail->addAddress('[email protected]');
$mail->Subject = 'PHPMailer SMTP test';
$mail->isHTML(true);
$mail->Body = '<p>It works.</p>';
$mail->AltBody = 'It works.';
$mail->send();
echo "Message sent\n";
} catch (Exception $e) {
echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}\n";
}
Send a test email
Run SMTP_USERNAME=smtp_xxx SMTP_PASSWORD=yourpass php send-test.php from the command line with a real recipient in addAddress(). Message sent means the relay accepted the message; check the inbox within a minute or two.
If it fails, uncomment $mail->SMTPDebug = SMTP::DEBUG_SERVER; and run again. The output shows every SMTP command and reply, so you can see whether the problem is the connection, the TLS handshake, authentication (535) or content (554 5.7.1). Turn debug off again before going to production.
Troubleshooting
| Problem | Fix |
|---|---|
| SMTP Error: Could not authenticate. | Wrong username or password, or getenv() returns false because the variables are not set for the PHP process. See https://bulko.io/en/smtp-errors/535/ |
| SMTP Error: Could not connect to SMTP host. | Port blocked by the host or wrong SMTPSecure for the port. Try Port 2525 with ENCRYPTION_STARTTLS, and check the debug output. |
| SSL routines: wrong version number | ENCRYPTION_SMTPS was used with port 587. Use ENCRYPTION_STARTTLS on 587, or switch Port to 465. |
| Connection hangs, then times out on 465 | ENCRYPTION_STARTTLS was used with port 465. Port 465 needs ENCRYPTION_SMTPS. |
| Class "PHPMailer\PHPMailer\PHPMailer" not found | The autoloader is not loaded or the package is not installed. Run composer require phpmailer/phpmailer and require vendor/autoload.php. |
| SMTP Error: data not accepted (554 5.7.1) | The AI content filter rejected the message for phishing, malware, impersonation or scam patterns. See https://bulko.io/en/acceptable-use/ |
FAQ
Should SMTPSecure be tls or ssl in PHPMailer?
Use PHPMailer::ENCRYPTION_STARTTLS (tls) with port 587 or 2525, and PHPMailer::ENCRYPTION_SMTPS (ssl) with port 465. The constants are clearer than the raw strings.
Why use PHPMailer instead of PHP mail()?
mail() hands the message to the local sendmail with no SMTP authentication, so it often lands in spam or is blocked by the host. PHPMailer authenticates to a real SMTP relay.
Can PHPMailer read incoming email from this server?
No. Bulko SMTP is outgoing only, with no IMAP or inbox. Replies go to the From or Reply-To address you set.
Is there a sending limit?
The default is 2,000 emails per day per SMTP account. Contact [email protected] to raise it.
How much does it cost?
One-time packages: $5 for 5,000 emails, $15 for 20,000, $30 for 50,000, $60 for 150,000. Unused emails never expire and there is no free SMTP trial.
Get SMTP credentials for PHPMailer
One-time payment by card or crypto, no subscription. Unused emails never expire. 2,000 emails per day per SMTP account.
What you can send: Acceptable Use Policy.