PHP Mailto Link Generator: Complete Guide

Learn how to generate perfect mailto links in PHP with proper encoding, validation, and framework integration.

Quick Start (Pure PHP)

<?php
function create_mailto($to, $subject = '', $body = '', $cc = '', $bcc = '') {
    $params = [];
    
    if (!empty($subject)) {
        $params[] = 'subject=' . rawurlencode($subject);
    }
    if (!empty($body)) {
        $params[] = 'body=' . rawurlencode($body);
    }
    if (!empty($cc)) {
        $params[] = 'cc=' . rawurlencode($cc);
    }
    if (!empty($bcc)) {
        $params[] = 'bcc=' . rawurlencode($bcc);
    }
    
    $query = !empty($params) ? '?' . implode('&', $params) : '';
    return "mailto:{$to}{$query}";
}

// Usage
$mailto = create_mailto(
    '[email protected]',
    'Bug Report',
    "Description:\n- Issue: Login failed\n- Browser: Chrome"
);

echo $mailto;
// mailto:[email protected]?subject=Bug%20Report&body=Description%3A%0A-%20Issue%3A%20Login%20failed%0A-%20Browser%3A%20Chrome
?>

Object-Oriented Approach

<?php
class MailtoBuilder {
    private $to;
    private $subject = '';
    private $body = '';
    private $cc = '';
    private $bcc = '';
    private $maxLength = 1800;
    
    public function __construct($to) {
        if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException("Invalid email address: {$to}");
        }
        $this->to = $to;
    }
    
    public function setSubject($subject) {
        $this->subject = $subject;
        return $this;
    }
    
    public function setBody($body) {
        $this->body = $body;
        return $this;
    }
    
    public function setCC($cc) {
        $this->cc = $cc;
        return $this;
    }
    
    public function setBCC($bcc) {
        $this->bcc = $bcc;
        return $this;
    }
    
    public function build() {
        $params = [];
        
        if (!empty($this->subject)) {
            $params[] = 'subject=' . rawurlencode($this->subject);
        }
        if (!empty($this->body)) {
            $params[] = 'body=' . rawurlencode($this->body);
        }
        if (!empty($this->cc)) {
            $params[] = 'cc=' . rawurlencode($this->cc);
        }
        if (!empty($this->bcc)) {
            $params[] = 'bcc=' . rawurlencode($this->bcc);
        }
        
        $query = !empty($params) ? '?' . implode('&', $params) : '';
        $mailto = "mailto:{$this->to}{$query}";
        
        if (strlen($mailto) > $this->maxLength) {
            throw new LengthException("Mailto URL too long: " . strlen($mailto) . " chars");
        }
        
        return $mailto;
    }
    
    public function getStats() {
        $mailto = $this->build();
        return [
            'total_length' => strlen($mailto),
            'max_length' => $this->maxLength,
            'remaining' => $this->maxLength - strlen($mailto),
            'percentage' => round((strlen($mailto) / $this->maxLength) * 100, 2)
        ];
    }
}

// Usage
$builder = new MailtoBuilder('[email protected]');
$mailto = $builder
    ->setSubject('Support Request')
    ->setBody("Hi Team,\n\nI need help with...")
    ->build();

echo $mailto;

// Check stats
print_r($builder->getStats());
?>

Laravel Integration

<?php
// app/Helpers/MailtoHelper.php
namespace App\Helpers;

class MailtoHelper {
    public static function create($to, $subject = '', $body = '', $cc = '', $bcc = '') {
        $params = collect([
            'subject' => $subject,
            'body' => $body,
            'cc' => $cc,
            'bcc' => $bcc
        ])->filter()->map(function($value, $key) {
            return "{$key}=" . rawurlencode($value);
        })->implode('&');
        
        $query = !empty($params) ? "?{$params}" : '';
        return "mailto:{$to}{$query}";
    }
}

// Controller usage
namespace App\Http\Controllers;

use App\Helpers\MailtoHelper;
use Illuminate\Http\Request;

class ContactController extends Controller {
    public function show() {
        $supportMailto = MailtoHelper::create(
            '[email protected]',
            'Support Request from Website',
            "Please describe your issue:\n\n"
        );
        
        return view('contact', compact('supportMailto'));
    }
    
    public function generateMailto(Request $request) {
        $validated = $request->validate([
            'to' => 'required|email',
            'subject' => 'nullable|string|max:200',
            'body' => 'nullable|string|max:1500',
        ]);
        
        $mailto = MailtoHelper::create(
            $validated['to'],
            $validated['subject'] ?? '',
            $validated['body'] ?? ''
        );
        
        return response()->json([
            'success' => true,
            'mailto' => $mailto,
            'length' => strlen($mailto)
        ]);
    }
}
?>

Laravel Blade Template

<!-- resources/views/contact.blade.php -->
@extends('layouts.app')

@section('content')
<div class="container">
    <h1>Contact Us</h1>
    
    <a href="{{ $supportMailto }}" class="btn btn-primary">
        📧 Email Support
    </a>
    
    <!-- Dynamic mailto generation -->
    <form id="mailto-form">
        <input type="email" name="to" placeholder="To" required>
        <input type="text" name="subject" placeholder="Subject">
        <textarea name="body" rows="5" placeholder="Message"></textarea>
        <button type="submit">Generate Mailto Link</button>
    </form>
    
    <div id="result"></div>
</div>

<script>
document.getElementById('mailto-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    
    const response = await fetch('/api/mailto', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRF-TOKEN': '{{ csrf_token() }}'
        },
        body: JSON.stringify(Object.fromEntries(formData))
    });
    
    const data = await response.json();
    document.getElementById('result').innerHTML = 
        `<a href="${data.mailto}">${data.mailto}</a>`;
});
</script>
@endsection

WordPress Integration

<?php
// functions.php or custom plugin

/**
 * Generate mailto link
 */
function wp_mailto_link($to, $args = []) {
    $defaults = [
        'subject' => '',
        'body' => '',
        'cc' => '',
        'bcc' => ''
    ];
    
    $args = wp_parse_args($args, $defaults);
    
    $params = [];
    foreach (['subject', 'body', 'cc', 'bcc'] as $key) {
        if (!empty($args[$key])) {
            $params[] = $key . '=' . rawurlencode($args[$key]);
        }
    }
    
    $query = !empty($params) ? '?' . implode('&', $params) : '';
    return esc_url("mailto:{$to}{$query}");
}

// Usage in template
$contact_link = wp_mailto_link('[email protected]', [
    'subject' => 'Contact from ' . get_bloginfo('name'),
    'body' => "Hi,\n\nI found your site..."
]);
?>

<a href="<?php echo $contact_link; ?>" class="contact-button">
    Email Us
</a>

<?php
// Shortcode version
function mailto_shortcode($atts) {
    $atts = shortcode_atts([
        'to' => get_option('admin_email'),
        'subject' => '',
        'body' => '',
        'text' => 'Email Us'
    ], $atts);
    
    $mailto = wp_mailto_link($atts['to'], $atts);
    
    return sprintf(
        '<a href="%s" class="mailto-link">%s</a>',
        esc_url($mailto),
        esc_html($atts['text'])
    );
}
add_shortcode('mailto', 'mailto_shortcode');

// Usage in post/page:
// [mailto to="[email protected]" subject="Help Request" text="Get Support"]
?>

Advanced Template System

<?php
class MailtoTemplate {
    private static $templates = [
        'support' => [
            'to' => '[email protected]',
            'subject' => 'Support Request: {issue}',
            'body' => "Hi Support Team,\n\nIssue: {issue}\nUser: {user}\n\nDetails:\n{details}"
        ],
        'sales' => [
            'to' => '[email protected]',
            'subject' => 'Sales Inquiry from {company}',
            'body' => "Company: {company}\nContact: {contact}\n\nInterest: {interest}"
        ]
    ];
    
    public static function render($template_name, $vars = []) {
        if (!isset(self::$templates[$template_name])) {
            throw new InvalidArgumentException("Template not found: {$template_name}");
        }
        
        $template = self::$templates[$template_name];
        
        // Replace variables
        foreach ($vars as $key => $value) {
            $template['subject'] = str_replace("{{$key}}", $value, $template['subject']);
            $template['body'] = str_replace("{{$key}}", $value, $template['body']);
        }
        
        return create_mailto(
            $template['to'],
            $template['subject'],
            $template['body']
        );
    }
}

// Usage
$mailto = MailtoTemplate::render('support', [
    'issue' => 'Login Error',
    'user' => '[email protected]',
    'details' => 'Cannot login after password reset'
]);

echo $mailto;
?>

Security Best Practices

<?php
// Sanitize and validate inputs
function safe_mailto($to, $subject = '', $body = '') {
    // Validate email
    if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
        throw new InvalidArgumentException('Invalid email address');
    }
    
    // Sanitize inputs (remove potential XSS)
    $subject = strip_tags($subject);
    $body = strip_tags($body);
    
    // Limit lengths
    $subject = substr($subject, 0, 200);
    $body = substr($body, 0, 1500);
    
    // Build mailto
    $mailto = create_mailto($to, $subject, $body);
    
    // Final length check
    if (strlen($mailto) > 1800) {
        throw new LengthException('Mailto URL too long');
    }
    
    return $mailto;
}

// Usage
try {
    $mailto = safe_mailto(
        $_POST['email'],
        $_POST['subject'],
        $_POST['message']
    );
    echo htmlspecialchars($mailto, ENT_QUOTES, 'UTF-8');
} catch (Exception $e) {
    echo 'Error: ' . htmlspecialchars($e->getMessage());
}
?>

Testing

<?php
// Using PHPUnit
use PHPUnit\Framework\TestCase;

class MailtoBuilderTest extends TestCase {
    public function testBasicMailto() {
        $mailto = create_mailto('[email protected]');
        $this->assertEquals('mailto:[email protected]', $mailto);
    }
    
    public function testWithSubject() {
        $mailto = create_mailto('[email protected]', 'Hello World');
        $this->assertStringContainsString('subject=Hello%20World', $mailto);
    }
    
    public function testLineBreaksEncoded() {
        $mailto = create_mailto('[email protected]', '', "Line 1\nLine 2");
        $this->assertStringContainsString('%0A', $mailto);
    }
    
    public function testInvalidEmail() {
        $this->expectException(InvalidArgumentException::class);
        new MailtoBuilder('invalid-email');
    }
    
    public function testLengthLimit() {
        $this->expectException(LengthException::class);
        $builder = new MailtoBuilder('[email protected]');
        $builder->setBody(str_repeat('x', 2000))->build();
    }
}
?>

Common Pitfalls

<?php
// ❌ WRONG: Using urlencode() instead of rawurlencode()
$mailto = "mailto:[email protected]?subject=" . urlencode($subject);
// urlencode converts spaces to +, which doesn't work in mailto

// ✅ CORRECT: Use rawurlencode()
$mailto = "mailto:[email protected]?subject=" . rawurlencode($subject);

// ❌ WRONG: Not handling special characters
$mailto = "mailto:[email protected]?subject={$subject}";

// ✅ CORRECT: Always encode
$mailto = "mailto:[email protected]?subject=" . rawurlencode($subject);

// ❌ WRONG: Forgetting to validate email
$mailto = create_mailto($_POST['email']);

// ✅ CORRECT: Validate first
if (filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
    $mailto = create_mailto($_POST['email']);
}
?>

API Endpoint Example

<?php
// api/mailto.php
header('Content-Type: application/json');

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['error' => 'Method not allowed']);
    exit;
}

$input = json_decode(file_get_contents('php://input'), true);

try {
    $mailto = safe_mailto(
        $input['to'] ?? '',
        $input['subject'] ?? '',
        $input['body'] ?? ''
    );
    
    echo json_encode([
        'success' => true,
        'mailto' => $mailto,
        'length' => strlen($mailto)
    ]);
} catch (Exception $e) {
    http_response_code(400);
    echo json_encode([
        'success' => false,
        'error' => $e->getMessage()
    ]);
}
?>

📚 Mailto Guides

💻 Other Language Guides

📖 Deep Dives


Test your PHP implementation with our free mailto link generator to ensure proper encoding.

← Back to Resources | Try Generator →

Have feedback? We'd love to hear it!