diff --git src/wp-includes/option.php src/wp-includes/option.php
index 6134606..da3e3a9 100644
--- src/wp-includes/option.php
+++ src/wp-includes/option.php
@@ -268,7 +268,8 @@ function update_option( $option, $value ) {
 	if ( $value === $old_value )
 		return false;
 
-	if ( false === $old_value )
+	/** This filter is documented in wp-includes/option.php */
+	if ( apply_filters( 'default_option_' . $option, false ) === $old_value )
 		return add_option( $option, $value );
 
 	$serialized_value = maybe_serialize( $value );
@@ -369,7 +370,8 @@ function add_option( $option, $value = '', $deprecated = '', $autoload = 'yes' )
 	// Make sure the option doesn't already exist. We can check the 'notoptions' cache before we ask for a db query
 	$notoptions = wp_cache_get( 'notoptions', 'options' );
 	if ( !is_array( $notoptions ) || !isset( $notoptions[$option] ) )
-		if ( false !== get_option( $option ) )
+		/** This filter is documented in wp-includes/option.php */
+		if ( apply_filters( 'default_option_' . $option, false ) !== get_option( $option ) )
 			return false;
 
 	$serialized_value = maybe_serialize( $value );
diff --git tests/phpunit/tests/option/option.php tests/phpunit/tests/option/option.php
index a866cea..847591c 100644
--- tests/phpunit/tests/option/option.php
+++ tests/phpunit/tests/option/option.php
@@ -59,6 +59,30 @@ class Tests_Option_Option extends WP_UnitTestCase {
 		$this->assertFalse( get_option( 'doesnotexist' ) );
 	}
 
+	/**
+	 * @ticket 31047
+	 */
+	public function test_add_option_should_respect_default_option_filter() {
+		add_filter( 'default_option_doesnotexist', array( $this, '__return_foo' ) );
+		$added = add_option( 'doesnotexist', 'bar' );
+		remove_filter( 'default_option_doesnotexist', array( $this, '__return_foo' ) );
+
+		$this->assertTrue( $added );
+		$this->assertSame( 'bar', get_option( 'doesnotexist' ) );
+	}
+
+	/**
+	 * @ticket 31047
+	 */
+	public function test_update_option_should_respect_default_option_filter_when_option_does_not_yet_exist_in_database() {
+		add_filter( 'default_option_doesnotexist', array( $this, '__return_foo' ) );
+		$added = update_option( 'doesnotexist', 'bar' );
+		remove_filter( 'default_option_doesnotexist', array( $this, '__return_foo' ) );
+
+		$this->assertTrue( $added );
+		$this->assertSame( 'bar', get_option( 'doesnotexist' ) );
+	}
+
 	function test_serialized_data() {
 		$key = rand_str();
 		$value = array( 'foo' => true, 'bar' => true );
