Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Dkim Laravel Package

simonschaufi/laravel-dkim

Add DKIM signing to outgoing Laravel mail with this simple package. Configure your domain, selector, and private key, and it automatically signs emails through Laravel’s mailer to improve authentication and deliverability.

View on GitHub
Deep Wiki
Context7

Getting Started

simonschaufi/laravel-dkim adds DKIM signing to Laravel's mail pipeline so you can authenticate outbound mail without changing any Mailable or Mail:: call sites.

  1. Install: composer require simonschaufi/laravel-dkim (v3.6.0 widens support through Laravel 13.x; check composer show simonschaufi/laravel-dkim for the actual illuminate/* constraint before bumping).
  2. Publish the config: php artisan vendor:publish --tag=config (or --provider="Simonschaufi\LaravelDkim\DkimServiceProvider").
  3. Fill in config/dkim.php — typically domain, selector, private_key (PEM contents or a path to the key), optional passphrase, and an enabled flag.
  4. Prefer env-driven values (DKIM_DOMAIN, DKIM_SELECTOR, DKIM_PRIVATE_KEY, DKIM_PASSPHRASE, DKIM_ENABLED) so each environment can differ, and keep the private key out of the repo (secret mount / storage/ path, not committed).
  5. Publish the DNS side: a TXT record at <selector>._domainkey.<domain> containing the public key (v=DKIM1; k=rsa; p=...), 2048-bit RSA, PEM key pair.
  6. Verify: send a real message through your configured mailer (Mailpit/Mailhog locally, real SMTP in staging) and inspect the raw source for a DKIM-Signature header, then confirm Authentication-Results: ... dkim=pass at the receiving end.

Where to look first: config/dkim.php for the knobs, the package's service provider for how the transport is decorated, and the README for the current config key names — those have shifted between major versions, so trust the installed version over blog posts.

First use case: you're sending transactional mail through a plain SMTP relay or a VPS that doesn't do provider-side DKIM signing, and you want Gmail/Outlook to stop marking it as spam. Drop in the package, add the DNS record, done — no application code changes.

Implementation Patterns

Environment-gated global signing. Enable in production/staging, disable locally where Mailpit doesn't care:

// config/dkim.php
'enabled' => env('DKIM_ENABLED', false),

Set DKIM_ENABLED=true only in the environments whose DNS you control. This is the lowest-friction pattern and covers 90% of apps.

Sign only some mailers. If you send marketing mail through a provider with its own Easy DKIM (SES, Postmark, SendGrid) and transactional mail through your own relay, keep the package scoped to the mailer that needs it. Define separate mailers in config/mail.php and let each transport resolve independently, rather than signing everything and creating double signatures.

Multi-domain / multi-tenant apps. The signer reads config at send time, so a runtime override before dispatch is workable:

config([
    'dkim.domain'   => $tenant->mail_domain,
    'dkim.selector' => $tenant->dkim_selector,
    'dkim.private_key' => $tenant->dkimPrivateKey(),
]);

Mail::to($user)->send(new Receipt($order));

Wrap this in a small DkimContext service (or a queued-job middleware) so it's applied consistently, and make sure queued mail re-applies it inside the job — the config override does not travel with the queued mailable.

Key rotation without downtime. Create a new selector (202501 → 202506), publish the new TXT record, wait for DNS propagation, deploy the config change, confirm dkim=pass on the new selector, then delete the old record. Never swap the key under an existing selector.

Testing. Mail::fake() intercepts before the transport, so you will not see a signature — don't write assertions against it. Instead:

  • Unit-test the signer directly by building a Symfony\Component\Mime\Email and asserting a DKIM-Signature header appears.
  • Integration-test with the array or log transport and parse the raw message, or run against Mailpit and read the .eml.
  • Keep one real-DNS smoke test in staging; header presence locally tells you nothing about DNS correctness.

Deploy checklist. php artisan config:cache, php artisan queue:restart (workers hold the old config/key in memory), and confirm the key file is readable by the www-data/queue user.

Gotchas and Tips

  • v3.6.0 is a compatibility-only release. Two "Laravel 13.x Compatibility" PRs (one automated from laravel-shift, one by the maintainer) — expect the illuminate/* / symfony/mailer constraints and the CI matrix to be widened, with no config or API changes. Treat it as a safe minor bump; the real risk is app-side, not package-side: verify your other mail-adjacent packages (custom transports, mail loggers, open/click tracking) also support Laravel 13 / the newer Symfony Mailer before upgrading the framework.
  • Config cache is the #1 false alarm. Changing DKIM_PRIVATE_KEY or DKIM_DOMAIN and seeing no effect almost always means a stale bootstrap/cache/config.php. Re-run config:cache (or config:clear locally).
  • Newlines in the private key via env. PEM keys are multi-line; .env values with literal \n frequently break openssl_pkey_get_private. Point private_key at a file path (secret mount, storage/app/dkim/private.key, permissions 600) instead of inlining the key.
  • Body rewrites after signing break the hash. Anything that mutates the message body or signed headers post-transport — click/open tracking, a footer-injecting middleware, an SMTP proxy — produces dkim=fail with body hash did not verify. Signing needs to be the last step before the wire.
  • Double signatures. If your provider already signs, you'll get two DKIM-Signature headers. Harmless when both pass, confusing when one fails. Disable the package for those mailers rather than debugging two keys.
  • Header canonicalization mismatches. If the receiver reports dkim=pass but the message "looks modified" or DMARC still fails, check alignment: the d= domain in the signature must align with your From: domain (or a subdomain under relaxed alignment), not just be a valid signature.
  • Queue workers and long-running processes. Octane and queue workers resolve the mailer once; a config or key swap needs queue:restart / Octane reload, otherwise you'll keep signing with the old key until the process recycles.
  • Mail::fake() gives false confidence. Covered above, but it's the most common reason teams think DKIM is configured when it isn't — signatures only exist at the transport layer.
  • Extension point for custom needs. If you need per-header or per-message control (e.g., signing only specific mailables), the package's signer is the seam to wrap: decorate it or bind your own implementation rather than forking. For one-off headers, $mailable->withSymfonyMessage(fn ($message) => ...) still works normally for non-signed headers.
  • Key/permissions failures are silent-ish. Unreadable or passphrase-protected keys surface as send-time exceptions or unsigned mail depending on where the failure lands; if mail "sends fine" but has no DKIM-Signature header, check the key path and storage permissions first.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo