Using VERP with swiftmailer, symfony and exim

VERP ( provides a method for handling bounced emails. This is especially useful when it comes to figuring out which emails failed to get delivered from your application/mail outs. This post covers how I got this working in symfony using swiftmailer to send the mail, and exim to deliver bounces back into symfony. The concepts shown can be applied to just about any combination of software though.

Sending an email with VERP

I used a task whilst testing this, and the concept is pretty simple. The code below is from the execute() method of my task

  protected function execute($arguments = array(), $options = array())
    $verp = 'bounces-' . str_replace('@', '=', $arguments['recipient']) . '';

    $message = $this->getMailer()->compose('[email protected]', $arguments['recipient'], 'Subject Line');


As can be seen above, all we are doing is setting a customised return path – firstly replacing the @ in the email address with a =, and also prefixing ‘bounces-‘ at the beginning. The resulting $verp variable looks like this:

  [email protected]

Now, we need to configure the MTA to pass incoming messages to PHP.

Configuring Exim

I could have have these emails sent from a catch-all domain, which then stores the messages in a POP3 account which a cron’ed script polls. However, I prefer to have the MTA deliver things directly into my app via a pipe. With exim, this is very simple – but you will need to understand the basics of exim so that you can place the configuration snippets below correctly within your own config.

The Router

This router can be modified to suit your needs. You’ll see I’m setting a local_part_prefix to the same prefix used above (‘bounces’), and I’m using a domain list of local_domains – you might want to change that.

  driver =  accept
  domains = +local_domains
  transport = my_pipe
  local_part_prefix = bounces-

The Transport

The transport used by the above router is responsible for piping the mail into a symfony task, which then processes it. Again it’s very simple, and you should check the exim manual for any other options you might want to set as part of your transport.

  driver = pipe
  command = /var/www/mysite/app/symfony nosp:email-incoming $local_part

The incoming email task

As you can see from the transport above, in my configuration the ‘local_part’ of the mail is passed as an argument to my script.

  protected function execute($arguments = array(), $options = array())
    // read stdin
    $content = file_get_contents('php://stdin');

    $log = fopen('/tmp/log.txt', 'a');
    fputs($log, $arguments['address'] . "n");
    fputs($log, "-----n");
    fputs($log, $content);
    fputs($log, "-----nnn");

From my code above, you can see that I’m writing out the mail to a file in /tmp, just so I can examine the output. the first line is the important line, and it contains the following (based on my example VERP address above).

How you handle bounces behond this is application specific – you might want to remove addresses from your list immediately, or only remove them if X out of the last Y mails didn’t make it though, or perhaps just raise an alert for further investigation.


There’s nothing complicated about the above, it’s a very simple solution – but it works. I would strongly suggest you employ this kind of bounce handling to your applications. If your server is constantly sending out email to invalid addresses, it’s likely to end up on blacklists, or your mails be marked as spam.

2 thoughts on “Using VERP with swiftmailer, symfony and exim”

  1. What does the complete header that’s passed to task look like?
    Can you easily handle various failure types?

    Is there any benefit from using a task over a local http connection for processing VERP responses?

    1. No – not really. I used a task because exim could easily deliver directly over a pipe. I could pipe to any kind of script, that could then make the HTTP request, but there would be little point in that if I’m having to fire up a script anyway. I’ve seen applications that do this though, parsing a mail from a pop3 mailbox, then POSTing it to a URL to process it. That’s more implementation detail than I wanted to put into the post, the important thing is getting some kind of delivery failure notification – how you go about handling that is best left up to you, as it’s likely to be application specific.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.