diff --git a/app/vmselect/netstorage/netstorage.go b/app/vmselect/netstorage/netstorage.go
index 100a44747a..40488de351 100644
--- a/app/vmselect/netstorage/netstorage.go
+++ b/app/vmselect/netstorage/netstorage.go
@@ -576,6 +576,7 @@ func setupTfss(tagFilterss [][]storage.TagFilter) ([]*storage.TagFilters, error)
 			}
 		}
 		tfss = append(tfss, tfs)
+		tfss = append(tfss, tfs.Finalize()...)
 	}
 	return tfss, nil
 }
diff --git a/lib/storage/tag_filters.go b/lib/storage/tag_filters.go
index ce3dcb148d..648bdec089 100644
--- a/lib/storage/tag_filters.go
+++ b/lib/storage/tag_filters.go
@@ -33,6 +33,8 @@ func NewTagFilters() *TagFilters {
 // Add adds the given tag filter to tfs.
 //
 // MetricGroup must be encoded with nil key.
+//
+// Finalize must be called after tfs is constructed.
 func (tfs *TagFilters) Add(key, value []byte, isNegative, isRegexp bool) error {
 	// Verify whether tag filter is empty.
 	if len(value) == 0 {
@@ -66,6 +68,34 @@ func (tfs *TagFilters) Add(key, value []byte, isNegative, isRegexp bool) error {
 	return nil
 }
 
+// Finalize finalizes tfs and may return complementary TagFilters,
+// which must be added to the resulting set of tag filters.
+func (tfs *TagFilters) Finalize() []*TagFilters {
+	var tfssNew []*TagFilters
+	for i := range tfs.tfs {
+		tf := &tfs.tfs[i]
+		if tf.matchesEmptyValue {
+			// tf matches empty value, so it must be accompanied with `key!~".+"` tag filter
+			// in order to match time series without the given label.
+			tfssNew = append(tfssNew, tfs.cloneWithNegativeFilter(tf))
+		}
+	}
+	return tfssNew
+}
+
+func (tfs *TagFilters) cloneWithNegativeFilter(tfNegative *tagFilter) *TagFilters {
+	tfsNew := NewTagFilters()
+	for i := range tfs.tfs {
+		tf := &tfs.tfs[i]
+		if tf == tfNegative {
+			tfsNew.Add(tf.key, []byte(".+"), true, true)
+		} else {
+			tfsNew.Add(tf.key, tf.value, tf.isNegative, tf.isRegexp)
+		}
+	}
+	return tfsNew
+}
+
 // String returns human-readable value for tfs.
 func (tfs *TagFilters) String() string {
 	if len(tfs.tfs) == 0 {
@@ -102,7 +132,7 @@ type tagFilter struct {
 
 	// Prefix always contains {nsPrefixTagToMetricIDs, key}.
 	// Additionally it contains:
-	//  - value ending with tagSeparatorChar if !isRegexp.
+	//  - value if !isRegexp.
 	//  - non-regexp prefix if isRegexp.
 	prefix []byte
 
@@ -111,6 +141,11 @@ type tagFilter struct {
 
 	// Matches regexp suffix.
 	reSuffixMatch func(b []byte) bool
+
+	// Set to true for filter that matches empty value, i.e. "", "|foo" or ".*"
+	//
+	// Such a filter must be applied directly to metricNames.
+	matchesEmptyValue bool
 }
 
 // String returns human-readable tf value.
@@ -196,6 +231,9 @@ func (tf *tagFilter) Init(commonPrefix, key, value []byte, isNegative, isRegexp
 	}
 	tf.orSuffixes = append(tf.orSuffixes[:0], rcv.orValues...)
 	tf.reSuffixMatch = rcv.reMatch
+	if len(prefix) == 0 && !tf.isNegative && tf.reSuffixMatch(nil) {
+		tf.matchesEmptyValue = true
+	}
 	return nil
 }