Perfex CRM Emails Going to Spam: SPF, DKIM and DMARC
Perfex CRM emails going to spam? The cause is DMARC alignment, not spam words. Fix SPF, DKIM and DMARC in the order that actually restores inbox placement.
A client rings to say they never received the invoice. You check Perfex: sent, three days ago. You check the provider’s outbound log: 250 OK, accepted. The client later finds it in Junk, under a newsletter they never subscribed to. Perfex CRM emails going to spam is the exact opposite failure to a broken SMTP connection - the transport worked perfectly, and the receiving mail server simply decided the message could not be trusted to be from you.
That decision comes down to three DNS records, and to whether they line up with the address in the From: header. Most self-hosted installs get one half-right, never publish the second, and read the third as a formality. The result is mail that is technically delivered and practically invisible.
We build and sell the Perfex modules named later in this article, so weigh the recommendations accordingly. The DNS work below needs none of them and applies to a stock install.
Read the Authentication-Results header before you touch DNS
Guessing at records is how people end up with two SPF entries and a worse problem. Send an invoice to a Gmail address you control, choose Show original, and read the top of the raw source:
Authentication-Results: mx.google.com;
spf=pass (google.com: domain of [email protected]
designates 203.0.113.9 as permitted sender)
[email protected];
dkim=none;
dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=yourdomain.com
Three verdicts, and two field names that explain everything. smtp.mailfrom is the envelope sender, the address the servers negotiate with and the one SPF checks. header.from is the address your client actually sees. Here SPF passed - for the hosting company. DKIM was never applied. DMARC then asked whether either mechanism passed for yourdomain.com, found neither, and failed.
A spf=pass line is not the good news it looks like.
Perfex CRM emails going to spam: why invoice mail lands in junk on a shared host
Perfex hands the message to whatever you set under Setup > Settings > Email. Where Email Protocol is left on the PHP mailer, it goes to the server’s local MTA, which stamps the envelope sender as the panel user at the machine’s own hostname - [email protected]. Your host publishes a valid SPF record for that domain, so SPF passes on behalf of a company you merely rent disk space from.
Nothing on that box signs mail with a DKIM key for your domain, because nobody generated one. You are sending unsigned mail, from an IP shared with several hundred other sites, carrying a From: domain that has authorised none of it. Filters that used to shrug stopped when Google and Yahoo tightened their bulk sender rules in February 2024: authentication, a one-click unsubscribe for bulk mail, and a spam complaint rate under 0.30% became conditions of entry rather than best practice.
The fix is not a spam-word checklist. It is making one of SPF or DKIM pass and match header.from.
SPF, DKIM and DMARC for a self-hosted CRM, in the order that fixes alignment
| Record | Where it lives | What it proves | Most common failure |
|---|---|---|---|
| SPF | TXT at yourdomain.com | This IP may send from this envelope domain | Two records, or 10+ DNS lookups |
| DKIM | TXT at selector._domainkey.yourdomain.com | Headers and body signed by your domain | No signer, so dkim=none |
| DMARC | TXT at _dmarc.yourdomain.com | SPF or DKIM passed and matched From: | p=reject before reading reports |
1. Pick one sending service and one From address per stream
Decide what actually sends your transactional mail. On a shared host the honest answer is usually an SMTP relay for CRM transactional email on port 587 or 2525, because you cannot publish DKIM keys for a mail server you do not administer. Set Email Protocol to SMTP, point it at that relay, and set Email to a real mailbox on your own domain - [email protected], never a free-mail address. You cannot sign for gmail.com, and Yahoo publishes p=reject, so a @yahoo.com sender gets your invoice refused outright rather than filtered.
2. SPF: authorise that service, once
One TXT record at the apex, beginning v=spf1. Publishing two is the most common self-inflicted wound: receivers treat it as a permanent error and score it as a fail.
v=spf1 include:relay.example.net ~all
Watch the 10 DNS lookup limit. Every include, a, mx and redirect costs at least one, and nested includes count too. A record that accumulated Google, Microsoft, a mailer and two legacy providers will quietly hit permerror. Check with dig +short TXT yourdomain.com, delete includes for services you dropped, and keep ~all while testing - move to -all once reports are clean.
SPF has a ceiling: it authenticates the envelope, and most relays put their own bounce domain there, so SPF alone often cannot satisfy DMARC alignment. The next step is not optional.
3. DKIM: get a signature that carries your domain
Generate the key pair inside your relay’s control panel, then publish the public half as a TXT record at selector._domainkey.yourdomain.com. A 2048-bit key often has to be split into quoted strings; providers differ on whether they do that for you. Verify with dig +short TXT s1._domainkey.yourdomain.com before moving on.
DKIM matters more than SPF for one practical reason: it survives forwarding. When a client auto-forwards your invoice to their bookkeeper, the forwarding server becomes the sending IP and SPF breaks by design. The signature travels with the message.
4. DMARC: start at p=none and let the reports argue
v=DMARC1; p=none; rua=mailto:[email protected]; fo=1
Relaxed alignment is the default, so bounces.yourdomain.com aligns with [email protected]. Strict alignment (aspf=s; adkim=s) demands an exact match and breaks most relay setups - leave it alone.
Sit at p=none for two weeks and read the aggregate reports before tightening. They will name senders you forgot: an old ticketing system, a staging install still mailing from production DNS. Then move to p=quarantine; pct=25, then full quarantine, then p=reject. Add sp= if subdomains need a different policy.
Per-staff SMTP multiplies the identities you must authorise
Stock Perfex sends every outgoing message through a single, system-wide SMTP configuration. One sending identity, one set of records, one thing to get right.
Our Advanced Email System removes that limitation: each non-admin staff member gets an independent SMTP account, on a different SMTP server if you want, configured from the Perfex settings area with no coding. Operationally that is often right - one person’s expired credential becomes a one-person outage, not a company-wide one. For deliverability it is a real trade-off: every account you add is another sending identity that must align with the From: address it carries.
The rule is simple: the domain a staff SMTP account authenticates as and the domain in that person’s From: address must be the same organisational domain. Put a contractor’s own provider behind [email protected] and you get DKIM signed for their domain, an envelope from their domain, a header.from of yours, and a clean DMARC failure no SPF edit will repair. Either bring them onto your domain’s mail service, or let their From: be their own domain.
If you run our Mailbox module alongside it, its sent mail uses Perfex’s own outgoing email settings, so fixing the global configuration fixes Mailbox sending too. Its OAuth2 sending path for Gmail and Outlook - the latter through SMTP XOAUTH2 against a single Exchange Online resource - hands delivery to infrastructure that already signs for a properly configured Workspace or Microsoft 365 domain, removing a class of alignment problems rather than solving them cleverly.
Keep invoice mail away from your broadcasts
Reputation accrues to a domain and IP in aggregate. One announcement to 4,000 leads with a 2% complaint rate will follow the invoice you send next morning from the same domain, and no DNS record protects you from that. Split the streams:
- Transactional - invoices, reminders, ticket replies - stays on the parent domain, on a relay chosen for consistency rather than volume.
- Bulk - announcements and campaigns - moves to a subdomain such as
news.yourdomain.comwith its own SPF and DKIM, governed bysp=in the parent DMARC record.
Our Custom SMS & Email Notifications module sits on the bulk side of that line, with recipient groups, scheduled sends and a message history logging every message with sent, failed and scheduled counts plus CSV export. Treat that failed count as a deliverability instrument, not an admin log: a rising failure rate on a broadcast is the earliest warning you get, well before a client tells you an invoice went missing. It also sends SMS through Clickatell, MSG91 or Twilio and WhatsApp through the official Twilio WhatsApp Business API - channels with entirely separate reputation, which is why the last rungs of a dunning ladder belong there rather than in a fourth email nobody will see.
What authentication cannot fix
Correct SPF, DKIM and DMARC get your mail considered, not delivered. They do not override:
- A cold sending IP. A new relay with no history is treated cautiously for weeks whatever your DNS says.
- A shared IP pool with bad neighbours. Cheap relays sell their neighbours’ reputation along with the service.
- Complaint and engagement rates. Filters weigh what recipients do with your mail far more heavily than what your records claim.
- Corporate gateways. A recipient’s appliance can quarantine perfectly authenticated mail on rules you will never see; the only remedy is asking them to allowlist you.
Do this next, in this order
- Send an invoice to a Gmail address you own and read
Authentication-Results. Note all three verdicts plus thesmtp.mailfromandheader.fromvalues. - Run
dig +short TXT yourdomain.com, confirm exactly one record startingv=spf1, and count its DNS lookups. - Generate DKIM keys in your relay, publish the selector, confirm with
dig. - Publish
v=DMARC1; p=none; rua=...and leave it alone for two weeks. - Re-send the test. Do not tighten to
p=quarantineuntil you seedmarc=passwithheader.from=yourdomain.com.
If step 1 shows dkim=none, stop reading about spam trigger words and go generate a key. That one record fixes more Perfex deliverability problems than everything else on this page combined.