diff --git a/fail2ban/server/failregex.py b/fail2ban/server/failregex.py
index d5c9345f..7aa5d3df 100644
--- a/fail2ban/server/failregex.py
+++ b/fail2ban/server/failregex.py
@@ -89,6 +89,11 @@ def mapTag2Opt(tag):
 	except KeyError:
 		return tag.lower()
 
+
+# alternate names to be merged, e. g. alt_user_1 -> user ...
+ALTNAME_PRE = 'alt_'
+ALTNAME_CRE = re.compile(r'^' + ALTNAME_PRE + r'(.*)(?:_\d+)?$')
+
 ##
 # Regular expression class.
 #
@@ -114,6 +119,14 @@ class Regex:
 		try:
 			self._regexObj = re.compile(regex, re.MULTILINE if multiline else 0)
 			self._regex = regex
+			self._altValues = {}
+			for k in filter(
+				lambda k: len(k) > len(ALTNAME_PRE) and k.startswith(ALTNAME_PRE),
+				self._regexObj.groupindex
+			):
+				n = ALTNAME_CRE.match(k).group(1)
+				self._altValues[k] = n
+			self._altValues = list(self._altValues.items()) if len(self._altValues) else None
 		except sre_constants.error:
 			raise RegexException("Unable to compile regular expression '%s'" %
 								 regex)
@@ -248,7 +261,16 @@ class Regex:
 	#
 
 	def getGroups(self):
-		return self._matchCache.groupdict()
+		if not self._altValues:
+			return self._matchCache.groupdict()
+		# merge alternate values (e. g. 'alt_user_1' -> 'user' or 'alt_host' -> 'host'):
+		fail = self._matchCache.groupdict()
+		#fail = fail.copy()
+		for k,n in self._altValues:
+			v = fail.get(k)
+			if v and not fail.get(n):
+				fail[n] = v
+		return fail
 
 	##
 	# Returns skipped lines.
diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py
index 5b9125ed..8326f049 100644
--- a/fail2ban/server/filter.py
+++ b/fail2ban/server/filter.py
@@ -692,43 +692,46 @@ class Filter(JailThread):
 
 		# Iterates over all the regular expressions.
 		for failRegexIndex, failRegex in enumerate(self.__failRegex):
-			if logSys.getEffectiveLevel() <= logging.HEAVYDEBUG: # pragma: no cover
-				logSys.log(5, "  Looking for failregex %d - %r", failRegexIndex, failRegex.getRegex())
-			failRegex.search(self.__lineBuffer, orgBuffer)
-			if not failRegex.hasMatched():
-				continue
-			# The failregex matched.
-			logSys.log(7, "  Matched failregex %d: %s", failRegexIndex, failRegex.getGroups())
-			# Checks if we must ignore this match.
-			if self.ignoreLine(failRegex.getMatchedTupleLines()) \
-					is not None:
-				# The ignoreregex matched. Remove ignored match.
-				self.__lineBuffer = failRegex.getUnmatchedTupleLines()
-				logSys.log(7, "  Matched ignoreregex and was ignored")
-				if not self.checkAllRegex:
-					break
-				else:
-					continue
-			if date is None:
-				logSys.warning(
-					"Found a match for %r but no valid date/time "
-					"found for %r. Please try setting a custom "
-					"date pattern (see man page jail.conf(5)). "
-					"If format is complex, please "
-					"file a detailed issue on"
-					" https://github.com/fail2ban/fail2ban/issues "
-					"in order to get support for this format.",
-					 "\n".join(failRegex.getMatchedLines()), timeText)
-				continue
-			self.__lineBuffer = failRegex.getUnmatchedTupleLines()
 			# retrieve failure-id, host, etc from failure match:
 			try:
+				if logSys.getEffectiveLevel() <= logging.HEAVYDEBUG: # pragma: no cover
+					logSys.log(5, "  Looking for failregex %d - %r", failRegexIndex, failRegex.getRegex())
+				failRegex.search(self.__lineBuffer, orgBuffer)
+				if not failRegex.hasMatched():
+					continue
+				# current failure data (matched group dict):
+				fail = failRegex.getGroups()
+				# The failregex matched.
+				logSys.log(7, "  Matched failregex %d: %s", failRegexIndex, fail)
+				# Checks if we must ignore this match.
+				if self.ignoreLine(failRegex.getMatchedTupleLines()) \
+						is not None:
+					# The ignoreregex matched. Remove ignored match.
+					self.__lineBuffer = failRegex.getUnmatchedTupleLines()
+					logSys.log(7, "  Matched ignoreregex and was ignored")
+					if not self.checkAllRegex:
+						break
+					else:
+						continue
+				if date is None:
+					logSys.warning(
+						"Found a match for %r but no valid date/time "
+						"found for %r. Please try setting a custom "
+						"date pattern (see man page jail.conf(5)). "
+						"If format is complex, please "
+						"file a detailed issue on"
+						" https://github.com/fail2ban/fail2ban/issues "
+						"in order to get support for this format.",
+						 "\n".join(failRegex.getMatchedLines()), timeText)
+					continue
+				# we should check all regex (bypass on multi-line, otherwise too complex):
+				if not self.checkAllRegex or self.getMaxLines() > 1:
+					self.__lineBuffer = failRegex.getUnmatchedTupleLines()
+				# merge data if multi-line failure:
 				raw = returnRawHost
 				if preGroups:
-					fail = preGroups.copy()
-					fail.update(failRegex.getGroups())
-				else:
-					fail = failRegex.getGroups()
+					currFail, fail = fail, preGroups.copy()
+					fail.update(currFail)
 				# first try to check we have mlfid case (caching of connection id by multi-line):
 				mlfid = fail.get('mlfid')
 				if mlfid is not None:
diff --git a/fail2ban/tests/samplestestcase.py b/fail2ban/tests/samplestestcase.py
index 5f0a447a..90ce0b7f 100644
--- a/fail2ban/tests/samplestestcase.py
+++ b/fail2ban/tests/samplestestcase.py
@@ -144,6 +144,7 @@ def testSampleRegexsFactory(name, basedir):
 		regexsUsedRe = set()
 
 		# process each test-file (note: array filenames can grow during processing):
+		faildata = {}
 		i = 0
 		while i < len(filenames):
 			filename = filenames[i]; i += 1;
@@ -195,6 +196,7 @@ def testSampleRegexsFactory(name, basedir):
 					regexList = flt.getFailRegex()
 
 					try:
+						fail = {}
 						ret = flt.processLine(line)
 						if not ret:
 							# Bypass if filter constraint specified:
@@ -246,8 +248,8 @@ def testSampleRegexsFactory(name, basedir):
 						regexsUsedIdx.add(failregex)
 						regexsUsedRe.add(regexList[failregex])
 					except AssertionError as e: # pragma: no cover
-						raise AssertionError("%s: %s on: %s:%i, line:\n%s" % (
-									fltName, e, logFile.filename(), logFile.filelineno(), line))
+						raise AssertionError("%s: %s on: %s:%i, line:\n%s\nfaildata:%r, fail:%r" % (
+									fltName, e, logFile.filename(), logFile.filelineno(), line, faildata, fail))
 
 		# check missing samples for regex using each filter-options combination:
 		for fltName, flt in self._filters.iteritems():