Properly sending contact form emails and how to do it in Django

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'])}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s