Ever since I decided to use SpamStopsHere (they’re excellent, by the way) I noticed that some emails sent to me by one of my site’s contact form didn’t pass their SPF check.
It turns out that apart from your normal “From” sender, an email message also has an envelope sender specified in the Return-Path header. The first is considered to be the author, the second refers to who is responsible for actually sending the message.
So my site sent those contact form emails to me using as the sender whatever the user specified as his email address in the form – in both the From and Return-Path path fields. Whenever the user’s email domain had SPF records installed, the validation would run the user’s email against my mail servers and thus obviously fail.
The solution then is to use as the Return-Path an address of your own. How to do this in Django? It’s really simple, but totally non-obvious. This is per Ticket 9214 and Changeset 9842 (so you need at least 1.1).
connection = SMTPConnection(fail_silently=fail_silently) headers = {'From': 'users@email.address'} # From-header from_email = 'bounce@mysite.com' # Return-Path header EmailMessage(subject, message, from_email, recipient_list, connection=connection, headers=headers).send()
Note that we need to work with “EmailMessage“ directly – django.core.mail.send_mail
doesn’t provide an option for custom headers.
If you are using django-contact-form, it unfortunately doesn’t support custom headers out of the box either, so you need to apply a patch. Then, the following should work:
class MyContactForm(AkismetContactForm): def __init__(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) self.subject = "Contact Form" self.template_name = "site/pages/contact_email.txt" # Return-Path header, e.g. bounce@mysite.com self.from_email = settings.DEFAULT_FROM_EMAIL # From-header self.headers = lambda: {'From': "%s " % (self.cleaned_data['name'], self.cleaned_data['email'])}