How To Track Email Open And Click In Laravel And PHP: A Guide To Email Tracking

Hello Friends,
In developer life, We deal with emails in our everyday life. We all want to know how we can track if an email is opened or not. If open Email is clicked by the user or not.
Today, I will tell you how we can track whether you open or clicked an email. I will tell you this with an example.
Let's start step by step.

1. Install Laravel 

Our First Step is to install Laravel Project. To install Laravel we need to run the following command in the CLI or Terminal.

composer create-project laravel/laravel track-email

This command will install a fresh laravel project in the track-email folder.

2. Create Model and migration file 

After Installing laravel we need to create the model and migration file to manage records. to create the model and migration file run the following command in the CLI or Terminal.

php artisan make:model Campaign -a

This command will create a Modal named Campaign and its related files. The output for this command will look like the below.

   INFO  Model [app/Models/Campaign.php] created successfully.  

   INFO  Factory [database/factories/CampaignFactory.php] created successfully.  

   INFO  Migration [database/migrations/2023_03_24_173335_create_campaigns_table.php] created successfully.  

   INFO  Seeder [database/seeders/CampaignSeeder.php] created successfully.  

   INFO  Request [app/Http/Requests/StoreCampaignRequest.php] created successfully.  

   INFO  Request [app/Http/Requests/UpdateCampaignRequest.php] created successfully.  

   INFO  Controller [app/Http/Controllers/CampaignController.php] created successfully.  

   INFO  Policy [app/Policies/CampaignPolicy.php] created successfully.  

After creating the files, We need to create the tables in the database. You can just copy and paste the below code.


use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
     * Run the migrations.
    public function up(): void
        Schema::create('campaigns', function (Blueprint $table) {

     * Reverse the migrations.
    public function down(): void

Now, Run the migration command to create the tables.

php artisan migrate


3. Create Routes

I will create all the functionality in the route file. Because this is a basic example. You can edit the route file as below.


use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Mail;

| Web Routes
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!

Route::get('/', function () {

    $emails = \App\Models\Campaign::all();
    foreach ($emails as $key => $email) {
        Mail::to($email->email)->send(new \App\Mail\Campaign($email->email));
    return view('mails.campaign');

    return response()->file(public_path("1x1.png"));

    // return redirect(request()->url);
    return redirect()->away(request()->url);


There are 3 routes in the route file. Let me explain all the functions in detail.
In the send-mail route, I am fetching all the records from the campaign table. I have created some demo records with the faker and factory. After that, I am using Mailer to send mail. I will explain the mail below in the detail.
We will use a trackable image in the mail. So, We can track if the mail is open or not. In the route, we will use a 1x1 px image in the email, So it will not affect the email layout and will help to track the mail. We will send the route response as the image.
In this route, We will track If any click is performed or not on the email. For this we will change all the Url In the Email will a specific URLs. So, Whenever any click is performed  It will redirect to a common URL. Where we will perform our operations. 

4. Create Mailable

For sending mail, I am using a mailable class to send the campaign email. To create a mailable run the following command in the CLI or Terminal.

php artisan make:mail Campaign

It will create a mailable class with the Campaign name. It will also create a blade file for the mail.

The campaign blade file will look like below.


namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class Campaign extends Mailable
    use Queueable, SerializesModels;

    public $email;
     * Create a new message instance.
    public function __construct($email)
        $this->email = $email;

     * Get the message envelope.
    public function envelope(): Envelope
        return new Envelope(
            subject: 'Campaign',

     * Get the message content definition.
    public function content(): Content
        return new Content(
            view: 'mails.campaign',

     * Get the attachments for the message.
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
    public function attachments(): array
        return [];

After this, We need to create a  Blade file for the mail. The most important part of the blade file is to include an image file to track whether Mail is open or not.

Include Image in the Email

<img src="{{ route('track_open',['email' => $email]) }}">

We will include the image file with the route we create in the web.php file. We will also pass email in the URL email parameter. So, We can track which user had opened the email.
Once any email will open, we will fetch the record and mark the email as opened.

5. Replace href link in the email blade

Now, We need to replace the tag href link. We will include all the links in a custom URL. So, Whenever any user will click on any link in the email. Then user will be redirect to a specific URL. Where we will mark the email as clicked.

<a href=" {{ route('track_click',['url' => '','email' => $email ]) }}" target="_blank">READ MORE</a>

We will pass the original Url and user email as the parameter in the route.
So, On click user will be first redirected to the track_click route. Where we will get the email and original URL. After marking the Email as a click. We will redirect the user to the original URL.
After doing all these steps. We can track email is opened or clicked event.
After all the steps, Our result may look something like this.

I hope this post will be helpful to you.
You can share your thought in the comments.