Bogofilter FAQ

Official Versions: In English or French or Italian or Bulgarian
Current maintainer since 2013: Matthias Andree <m-a@users.sf.net>
Previous maintainer of ten years: David Relson <relson@osagesoftware.com>

This document is intended to answer frequently asked questions about bogofilter.

Typographic conventions

Frequently asked questions and their answers


What is bogofilter?

Bogofilter is a fast Bayesian spam filter along the lines suggested by Paul Graham in his article A Plan For Spam. bogofilter uses Gary Robinson's geometric-mean algorithm with the Fisher's method modification to classify email as spam or non-spam.

The bogofilter home page at SourceForge is the central clearinghouse for bogofilter resources.

Bogofilter was started by Eric S. Raymond on August 19, 2002. It gained popularity in September 2002, and a number of other authors have started to contribute to the project.

The NEWS file describes bogofilter's version history starting with version 1.0.0. Older news (before release 1.0.0) are in the NEWS.0 file.


Bogo-what?

Bogofilter is some kind of a bogometer or bogon filter, i.e., it tries to identify bogus mail by measuring the bogosity.


How does bogofilter work?

See the man page's THEORY OF OPERATION section for an introduction. The main source for understanding this is Gary Robinson's Linux Journal article "A Statistical Approach to the Spam Problem".

After you read all this you might ask some questions. The first could be "Is bogofilter really a Bayesian spam filter?" Bogofilter is based on Bayes' theorem and uses it in the initial calculations and other statistical methods later. Without doubt it is a statistical spam filter with a Bayesian flavor.

Other questions you might have might concern the basic assumptions of Bayes' theory. Two short answers are: "No, they are not satisfied" and "We don't care as long as it works". A longer answer will mention that the basic assumption that "an e-mail is a random collection of words, each independent of the others" is violated. There are several places where practice doesn't follow theory. Some are always present, and some which will depend on the way you use bogofilter:

As the man page explains, bogofilter tries to understand how badly the null hypothesis fails. Some people argue that "those departures from reality usually work in our favor" (from Gary's article). Some argue that, even then, we should not violate too much. Nobody really knows. Just keep in mind that problems might occur if you push too hard. The key to bogofilter's approach is: What matters most is simply what works in the real world.

Now that you have been warned, have fun and use bogofilter as suits you best.


Mailing Lists

There are currently four mailing lists for bogofilter:

List Address Links Description
bogofilter-announce@bogofilter.org [subscribe] [archives: mailman] An announcement-only list where new versions are announced.
bogofilter@bogofilter.org [subscribe] [archives: mailman] A discussion list where any conversation about bogofilter may take place.
bogofilter-dev@bogofilter.org [subscribe] [archives: mailman] A list for sharing patches, development, and technical discussions.
bogofilter-cvs@lists.sourceforge.net [subscribe] [archive] Mailing list for announcing code changes to the SVN archive. (The CVS name is a leftover from before the migration for our users' convenience.)

The bogofilter-announce list is moderated and is used only for important announcements (eg: new versions). It is low traffic. If you have subscribed to the user's list or the developer's list, you don't need to subscribe to the announce list. Messages posted to the announce list are also distributed to the others.


How do I start my bogofilter training?

To classify messages as ham (non-spam) or spam, bogofilter needs to learn from your mail. To start with it is best to have collections (that are as large as possible) of messages you know for sure are ham or spam. (Errors here will cause problems later, so try hard;-). Warning: Only use your mail; using other collections (like a spam collection found on the web), might cause bogofilter to draw a wrong conclusion — after all you want it to understand your mail.

Once you have the spam and ham collections, you have basically four choices. In all cases it works better if your training base (the above collections) is bigger, rather than smaller. The smaller your training collection is, the higher the number of errors bogofilter will make in production. Let's assume your collection is two mbox files: ham.mbox and spam.mbox.

Note: Bogofilter's contrib directory includes two scripts that both use a train-on-error technique. This technique scores each message and adds to the database only those messages that were scored incorrectly (messages scored as uncertain, ham scored as spam, or spam scored as ham). The goal is to build a database of those words needed to correctly classify messages. The resulting database is smaller than the one build using full training.

Comparing these methods

It is important to understand the consequences of the methods just described. Doing full training as in methods 1 and 4 produces a larger database than does training with methods 2 or 3. If your database size needs to be small (for example due to quota limitations), use methods 2 or 3.

Full training with method 1 is fastest. Training on error (as in methods 2, 3 and 4) is effective, but the initial training takes longer.


How do I train using maildirs?

Initial training from mbox:

    bogofilter -M -s -I ~/mail/Spam
    bogofilter -M -n -I ~/mail/NonSpam

Initial training from maildir:

    bogofilter -s -B ~/Maildir/.Spam
    bogofilter -n -B ~/Maildir/.NonSpam

Corrective training from mbox:

    bogofilter -M -Ns -I ~/mail/Missed_Spam
    bogofilter -M -Sn -I ~/mail/False_Spam

Corrective training from maildir:

    bogofilter -s -B ~/Maildir/.Missed_Spam
    bogofilter -n -B ~/Maildir/.False_Spam

How can I keep the scoring accuracy high?

Bogofilter will make mistakes once in a while. So ongoing training is important. There are two main methodologies for doing this. First, you can train with every incoming message (using the -u option). Second, you can train on error only.

Since you might want to rebuild your database at some point, for example when a major new feature is implemented in bogofilter, it can be very useful to update your training collection continuously.

Bogofilter always does the best it can with the information available to it. However, it will make mistakes, i.e., classify ham as spam (false positives) or spam as ham (false negatives). To reduce the likelihood of repeating the mistake, it is necessary to train bogofilter with the errant message. If a message is incorrectly classified as spam, use switch -n to train with it as ham. Use switch -s to train with a spam message.

Bogofilter has a -u switch that automatically updates the wordlists after scoring each message. As bogofilter sometimes misclassifies a message, monitoring is necessary to correct any mistakes. Corrections can be done using -Sn to change a message's classification from spam to non-spam and -Ns to change it from non-spam to spam.

Correcting a misclassified message may affect classification for other message. The smaller your database is, the higher is the likelihood that a training error will cause a misclassification.

Using a method like #2 or #3 (above) can compensate for this effect. Repeat the training with your complete training collection (including all the new messages added since the earlier training). This will add messages to the database which show that adverse effect on both sides until you have a new equilibrium.

An alternative strategy, based on method 4 in the previous section, is the following: Periodically take blocks of messages and use the scripts in method 4 above to classify them. Then manually review the good, bad and unsure files, correct any errors, and split the unsures into spam and non-spam. Until you have accumulated some 10,000 spam and 10,000 non-spam in your training database, train with the good, the bad, and the separated errors and unsures; thereafter, train with only the separated and unsures, discarding the messages that bogofilter already classifies correctly.


What mailbox (file) formats does bogofilter understand?

Bogofilter understands the traditional Unix mbox format, the Maildir and MH formats. Note though that bogofilter does not support subfolders, you will have to explicitly list them in MH or Maildir++ folders - just mention the full path to the subfolder.

For unsupported formats, you will have to convert the mailbox to a format bogofilter understands. Mbox is often convenient because it can be piped into bogofilter.

For example, to convert UW-IMAP/PINE mbx format to mbox:

    mailtool copy /full/path/to/mail.mbox '#driver.unix//full/path/to/mbox'

or:

    for MSG in /full/path/to/maildir/* ; do 
        formail -I Status: < "$MSG" >> /full/path/to/mbox
    done

What does bogofilter's verbose output mean?

Bogofilter can instructed to display information on the scoring of a message by running it with flags "-v", "-vv", "-vvv", or "-R".


What is Unsure mode?

Bogofilter's default configuration will classify a message as spam or non-spam. The SPAM_CUTOFF parameter is used for this. Messages with scores greater than or equal to SPAM_CUTOFF are classified as spam. Other messages are classified as ham.

There is also a HAM_CUTOFF parameter. When used, messages must have scores less than or equal to HAM_CUTOFF to be classified as ham. Messages with scores between HAM_CUTOFF and SPAM_CUTOFF are classified as unsure. If you look in bogofilter.cf, you will see the following lines:

    #### CUTOFF Values
    #
    #    both ham_cutoff and spam_cutoff are allowed.
    #    setting ham_cutoff to a non-zero value will
    #    enable tri-state results (Spam/Ham/Unsure).
    #
    #ham_cutoff  = 0.45
    #spam_cutoff = 0.99
    #
    #    for two-state classification:
    #
    ## ham_cutoff = 0.00
    ## spam_cutoff= 0.99

To turn on Yes/No/Unsure classification, remove the #'s from the last two lines.

Alternatively, if you'd rather use labels Yes/No/Unsure instead of Spam/Ham/Unsure, remove the #'s from the following bogofilter.cf line:

    ## spamicity_tags = Yes, No, Unsure

Once that's done, you may want to set the filtering rules for your mail program to include rules like:

    if header contains "X-Bogosity: Spam", put in Spam folder
    if header contains "X-Bogosity: Unsure", put in Unsure folder

Alternatively, bogofilter.cf has directives for modifying the Subject: line, i.e.

    #### SPAM_SUBJECT_TAG
    #
    #    tag added to "Subject: " line for identifying spam or unsure
    #    default is to add nothing.
    #
    ##spam_subject_tag=***SPAM***
    ##unsure_subject_tag=???UNSURE???

With these subject tags, the filtering rules would look like:

    if subject contains "***SPAM***", put in Spam folder
    if subject contains "???UNSURE???", put in Unsure folder

What are "training on error" and "training to exhaustion"?

"Training on error" involves scanning a corpus of known spam and non-spam messages; only those that are misclassified, or classed as unsure, get registered in the training database. It's been found that sampling just messages prone to misclassification is an effective way to train; if you train bogofilter on the hard messages, it learns to handle obvious spam and non-spam too.

This method can be enhanced by using a "security margin". By increasing the spam cutoff value and decreasing the ham cutoff value, messages which are close to a cutoff will be used for training. Using security margins improves results when training on error. In general, greater margins help more (although too much also isn't optimal). As a rule of thumb spam cutoff +/- 0.3 gives good results. For tristate mode, you might try the middle of the unsure interval +/- 0.3 for training.

Repeating training on error on the same message corpus can improve accuracy. The idea is that messages which were rated correctly in the first place might after some more training be rated wrongly which will then be corrected.

"Training to exhaustion" is repeating training on error, with the same message corpus, until no errors remain. Also this method can be improved with security margins. See Gary Robinson's Rants on this topic for more details.

Note: bogominitrain.pl has a -f option to do "training to exhaustion". Using -fn avoids repeated training for each message.


What does the '-u' (autoupdate) switch do?

The "-u" switch (autoupdate) is used to automatically expand the wordlist. When this switch is used and bogofilter classifies a message as Spam or Ham, the message's tokens are added to the wordlist with a ham/spam tag (as appropriate).

As an example, suppose a new "Refinance now - best Mortgage rates" message comes in. It will have some words that bogofilter has seen and (probably) some new ones as well. Using '-u' the new words will be added to the wordlist so that bogofilter can better recognize the next, related message.

If/when you use to use '-u', you need to be on the lookout for classification errors and retrain bogofilter with any messages that have been classified incorrectly. An incorrectly classified message that is auto-updated _may_ cause bogofilter to make additional classification errors in the future. This is the same problem as when you (the sys admin) incorrectly register a ham message as spam (or vice versa).


How can I use SpamAssassin to train Bogofilter?

If you have a working SpamAssassin installation (or care to create one), you can use its return codes to train bogofilter. The easiest way is to create a script for your MDA that runs SpamAssassin, tests the spam/non-spam return code, and runs bogofilter to register the message as spam (or non-spam). The sample procmail recipe below shows one way to do this:

    BOGOFILTER     = "/usr/bin/bogofilter"
    BOGOFILTER_DIR = "training"
    SPAMASSASSIN  = "/usr/bin/spamassassin"

    :0 HBc
    * ? $SPAMASSASSIN -e
    #spam yields non-zero
    #non-spam yields zero
    | $BOGOFILTER -n -d $BOGOFILTER_DIR
    #else (E)
    :0Ec
    | $BOGOFILTER -s -d $BOGOFILTER_DIR

    :0fw
    | $BOGOFILTER -p -e

    :0:
    * ^X-Bogosity:.Spam
    spam

    :0:
    * ^X-Bogosity:.Ham
    non-spam

What can I do about Asian spam?

Many people get unsolicited email using Asian language charsets. Since they don't know the languages and don't know people there, they assume it's spam.

The good news is that bogofilter does detect them quite successfully. The bad news is that this can be expensive. You have basically two choices:


How can I compact my database?

You can periodically compact the database so it occupies a minimum of disk space. Assuming your wordlist is in directory ~/.bogofilter, for bogofilter 0.93.0 (or newer) use:

    bf_compact ~/.bogofilter wordlist.db

For bogofilter older than 0.93.0, use:

    cd ~/.bogofilter
    bogoutil -d wordlist.db | bogoutil -l wordlist.db.new
    mv wordlist.db wordlist.db.prv
    mv wordlist.db.new wordlist.db

The script is needed to duplicate your database environment (in order to support BerkeleyDB transaction processing). Your original directory will be renamed to ~/.bogofilter.old and ~/.bogofilter will contain the new database environment.

Since older versions of bogofilter don't use Berkeley DB transactions, the database is just a single file (wordlist.db) and it isn't necessary to use the script. The commands shown above create a new compact database and rename the original file to wordlist.db.prv

Note: it's O.K. to use the script with old versions of bogofilter.


How do I manually query the database?

To find the spam and ham counts for a token (word) use bogoutil's '-w' option. For example, "bogoutil -w $BOGOFILTER_DIR/wordlist.db example.com" gives the good and bad counts for "example.com".

If you want the spam score in addition to the spam and ham counts for a token (word) use bogoutil's '-p' option. For example, "bogoutil -p $BOGOFILTER_DIR/wordlist.db example.com" gives the good and bad counts for "example.com".

To find out how many messages are in your wordlists query the special token .MSG_COUNT, i.e., run command "bogoutil -w $BOGOFILTER_DIR/wordlist.db .MSG_COUNT" to see the counts for the spam and ham wordlists.

To tell how many tokens are in your wordlists pipe the output of bogoutil's dump command to command "wc", i.e. use "bogoutil -d $BOGOFILTER_DIR/wordlist.db | wc -l " to display the count.


Can I use multiple wordlists?

Yes. Bogofilter can be run with multiple wordlists. For example, if you have both user and system wordlists, bogofilter can be instructed to check the user list and, if the word isn't there, then check the system list. Alternatively, it can be instructed to add together the information from the two lists.

Following are the config file options and some examples:

A wordlist has several attributes, notably type, name, filename, and precedence.

Example 1 - merge user and system lists:

    wordlist R,user,~/wordlist.db,1
    wordlist R,system,/var/spool/bogofilter/wordlist.db,1

Example 2 - prefer user to system list:

    wordlist R,user,~/wordlist.db,2
    wordlist R,system,/var/spool/bogofilter/wordlist.db,3

Example 3 - prefer system to user list:

    wordlist R,user,~/wordlist.db,5
    wordlist R,system,/var/spool/bogofilter/wordlist.db,4

Note 1: bogofilter's registration flags ('-s', '-n', '-u', '-S', '-N' ) will apply to the lowest numbered list.

Note 2: having lists of types 'R' and 'I' of the same precedence won't be allowed because the types are contradictory.


Can I tell bogofilter to ignore certain tokens?

Through the use of an ignore list, bogofilter will ignore the listed tokens when scoring the message.

Example:

    wordlist I,ignore,~/ignorelist.db,7
    wordlist R,system,/var/spool/bogofilter/wordlist.db,8

Because ignorelist.db has a lower index (7), than wordlist.db (8), bogofilter will stop looking when finds a token in ignorelist.db.

Note: Technically, bogofilter gives a score of ROBX to the tokens and expects the min_dev parameter to drop them from the scoring.

There are two main methods for building/maintaining an ignore list.

First, a text file can be created and maintained using any text editor. Bogoutil can convert the text file to database format, e.g. "bogoutil -l ignorelist.db < ignorelist.txt".

Alternatively, echo ... | bogoutil ... can be used to add a single token, for example "ignore.me", as in:

  echo ignore.me | bogoutil -l ~/ignorelist.db

How do I upgrade from separate word databases to the combined wordlist format?

Run script bogoupgrade. For more info, run "bogoupgrade -h" to see its help message or run "man bogoupgrade" and read its man page.


How can I tell if my wordlists are corrupted?

NOTE: some distributors rename all the db_ utilities given below by inserting or appending the version number, with or without dot, for instance db4.1_verify or db_verify-4.2. There is no standard on the renaming of these utilities.

If you think your wordlists are hosed, you can see what BerkeleyDB thinks by running:

    db_verify wordlist.db

You may be able to recover some (or all) of the tokens and their counts with the following commands:

    bogoutil -d wordlist.db | bogoutil -l wordlist.new.db

or - if there has been more damage to the token list - with

    db_dump -r wordlist.db > wordlist.txt
    db_load wordlist.new.db < wordlist.txt

You can also use a text file instead of a pipe, as in:

    bogoutil -d wordlist.db > wordlist.txt
    bogoutil -l wordlist.db.new < wordlist.txt

How can I convert my wordlist to/from unicode?

Wordlists can be converted from raw storage to unicode using:

    bogoutil -d wordlist.db > wordlist.raw.txt
    iconv -f iso-8859-1 -t utf-8 < wordlist.raw.txt > wordlist.utf8.txt
    bogoutil -l wordlist.db.new < wordlist.utf8.txt

or:

    bogoutil --unicode=yes -m wordlist.db

Wordlists can be converted from unicode to raw storage using:

    bogoutil -d wordlist.db > wordlist.utf8.txt
    iconv -f utf-8  -t iso-8859-1 < wordlist.utf8.txt > wordlist.raw.txt
    bogoutil -l wordlist.db.new < wordlist.raw.txt

or:

    bogoutil --unicode=no -m wordlist.db

The above methods work best when the wordlist is based on the iso-8859-1 charset. If your wordlist is based on a different charset, for example CP866 or KOI8-R, use that charset in the above commands.

For a wordlist containing tokens from multiple languages, particularly non-european languages, the conversion methods described above may not work well. Building a new wordlist (from scratch) will likely work better as the new wordlist will be based solely on unicode.


How can I switch from non-transaction to transaction mode?

How to do this is fully documented in file doc/README.db section 2.2.1. We suggest you read the whole section.

In brief, use these commands:

    cd ~/.bogofilter
    bogoutil -d wordlist.db > wordlist.txt
    mv wordlist.db wordlist.db.old
    bogoutil --db-transaction=yes -l wordlist.db < wordlist.txt

If everything went well, you can remove the backup files:

    rm wordlist.db.old wordlist.txt

How can I switch from transaction to non-transaction mode?

How to do this is fully documented in file doc/README.db section 2.2.2. We suggest you read the whole section.

In brief, you can use bogoutil to dump/load the wordlist, for example:

    cd ~/.bogofilter
    bogoutil -d wordlist.db > wordlist.txt
    mv wordlist.db wordlist.db.old
    rm -f log.?????????? __db.???
    bogoutil --db-transaction=no -l wordlist.db < wordlist.txt

Why does bogofilter die after printing "Lock table is out of available locks" or "Lock table is out of available object entries"

The transactional and concurrent modes of BerkeleyDB require a lock table that corresponds to the data base in size. See the README.db file for a detailed explanation and a remedy.

The size of the lock table can be set in bogofilter.cf or in DB_CONFIG. Bogofilter.cf uses the db_lk_max_locks and db_lk_max_objects directives, while DB_CONFIG uses the set_lk_max_objects and set_lk_max_locks directives.

After changing these values in DB_CONFIG, run command

  bogoutil --db-recover /your/bogofilter/directory

to rebuild the lock tables.


Why am I getting DB_PAGE_NOTFOUND messages?

You have a problem with your BerkeleyDB database. There are two likely causes: either you've hit a max size limit or the database is corrupt.

Some mail transfer agents, such as Postfix, impose file size limits. When bogofilter's database reaches that limit, write problems will occur.

To show the database size use:

    ls -lh $BOGOFILTER_DIR/wordlist.db

To show the postfix setting:

    postconf | grep mailbox_size_limit

To set the limit to 73MB (or whatever size is right for you):

    postconf -e mailbox_size_limit=73000000

If you think your database may be corrupt, read How can I tell if my wordlists are corrupted? FAQ entry.


Why am I getting "Berkeley DB library configured to support only DB_PRIVATE environments" or
"Berkeley DB library configured to support only private environments"?

Some distributors (for instance the Fedora Project) package Berkeley DB with support for POSIX threading and hence POSIX mutexes, but your system does not support POSIX mutexes (whether it does, depends on the kernel version and exact processor type).

To work around this problem:

  1. download, compile and install Berkeley DB on your own and the reconfigure bogofilter:
    1. cd build_unix
    2. ../dist/configure --enable-cxx
    3. make
    4. make install
  2. recompile and install bogofilter:
    1. ./configure --with-libdb-prefix=/usr/local/BerkeleyDB.4.3 (replace your Berkeley DB version number)
    2. make && make check
    3. make install (if space is a premium, use make install-strip)

Can bogofilter be used in a multi-user environment?

Yes, it can. There are multiple, distinct strategies for doing this. The two extremes are:

As a middle ground, the bogofilter administrator can create and maintain the global wordlists and each user can be given the choice of using the global wordlist or a private wordlist. An MDA, such as procmail, can be programmed to first apply the global wordlist (with a very stringent spam cutoff) and then (if necessary) apply the user's wordlist.


Can I share wordlists over NFS?

If you're just reading from them, there are no problems. When you're updating them, you need to use the correct file locking to avoid data corruption. When you compile bogofilter, you will need to verify that the configure script has set "#define HAVE_FCNTL 1" in your config.h file. Popular UNIX ope