diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 5ed78165373b5780d9ee3d56ae6cc75c1bcfe00d..d81e9d6f8fd409fefa023beb72991eec2610208b 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -2180,6 +2180,25 @@ void D_PickVote(void)
 	SendNetXCmd(XD_PICKVOTE, &buf, 2);
 }
 
+/*
+Return the number of times a series of keywords, delimited by spaces, matched.
+*/
+static int measurekeywords(const char *s, const char *q)
+{
+	int r = 0;
+	char *qp;
+	for (qp = strtok(va("%s", q), " ");
+			qp;
+			qp = strtok(0, " "))
+	{
+		if (strcasestr(s, qp))
+		{
+			r++;
+		}
+	}
+	return r;
+}
+
 /*
 Easy macro; declare parm_*id* and define acceptableargc; put in the parameter
 to match as a string as *name*. Set *argn* to the number of extra arguments
@@ -2218,8 +2237,6 @@ static void Command_Map_f(void)
 	char   *apromapname = NULL;
 
 	/* Keyword matching */
-	char *query;
-	char *key;
 	UINT8 *freq;
 	UINT8 freqc;
 
@@ -2319,13 +2336,13 @@ static void Command_Map_f(void)
 		}
 		else
 		{
-			query = ZZ_Alloc(strlen(mapname)+1);
 			freq = ZZ_Calloc(NUMMAPS * sizeof (UINT8));
 
 			for (i = 0, newmapnum = 1; i < NUMMAPS; ++i, ++newmapnum)
 				if (mapheaderinfo[i])
 			{
-				realmapname = G_BuildMapTitle(newmapnum);
+				if (!( realmapname = G_BuildMapTitle(newmapnum) ))
+					continue;
 
 				/* Now that we found a perfect match no need to fucking guess. */
 				if (strnicmp(realmapname, mapname, mapnamelen) == 0)
@@ -2345,16 +2362,9 @@ static void Command_Map_f(void)
 					}
 					else/* ...match individual keywords */
 					{
-						strcpy(query, mapname);
-						for (key = strtok(query, " ");
-								key;
-								key = strtok(0, " "))
-						{
-							if (strcasestr(realmapname, key))
-							{
-								freq[i]++;
-							}
-						}
+						freq[i] += measurekeywords(realmapname, mapname);
+						freq[i] += measurekeywords(mapheaderinfo[i]->keyword,
+								mapname);
 					}
 				}
 
@@ -2385,7 +2395,6 @@ static void Command_Map_f(void)
 			}
 
 			Z_Free(freq);
-			Z_Free(query);
 		}
 	}
 
diff --git a/src/dehacked.c b/src/dehacked.c
index 11aed24d043415d019bb048bd0be0e2d29c11a3a..7dfedde5db7b9ca71f9cfbf82143b9cb8167b1c8 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -1191,6 +1191,11 @@ static void readlevelheader(MYFILE *f, INT32 num)
 					mapheaderinfo[num-1]->typeoflevel = tol;
 				}
 			}
+			else if (fastcmp(word, "KEYWORD"))
+			{
+				deh_strlcpy(mapheaderinfo[num-1]->keyword, word2,
+						sizeof(mapheaderinfo[num-1]->keyword), va("Level header %d: keyword", num));
+			}
 			else if (fastcmp(word, "MUSIC"))
 			{
 				if (fastcmp(word2, "NONE"))
diff --git a/src/doomstat.h b/src/doomstat.h
index 9ae2726d7e4e7c173d7d5538e287ca8413611513..43ebaf0884edfca38193a1b0a5aec12d64da8e57 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -226,6 +226,7 @@ typedef struct
 	char actnum[3];        ///< SRB2Kart: Now a 2 character long string.
 	UINT16 typeoflevel;    ///< Combination of typeoflevel flags.
 	INT16 nextlevel;       ///< Map number of next level, or 1100-1102 to end.
+	char keyword[33];      ///< Keywords separated by space to search for. 32 characters.
 	char musname[7];       ///< Music track to play. "" for no music.
 	UINT16 mustrack;       ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore.
 	char forcecharacter[17];  ///< (SKINNAMESIZE+1) Skin to switch to or "" to disable.
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 0bb9a99d69e96a71c7beab3f3356aa34906f5215..d7a4fdaf4b29ee6597ec03b34e1d68c958a87e91 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -1473,6 +1473,8 @@ static int mapheaderinfo_get(lua_State *L)
 		lua_pushinteger(L, header->typeoflevel);
 	else if (fastcmp(field,"nextlevel"))
 		lua_pushinteger(L, header->nextlevel);
+	else if (fastcmp(field,"keyword"))
+		lua_pushstring(L, header->keyword);
 	else if (fastcmp(field,"musname"))
 		lua_pushstring(L, header->musname);
 	else if (fastcmp(field,"mustrack"))
diff --git a/src/p_setup.c b/src/p_setup.c
index 58e13c2e0d6d248978cb861532b2d6532f7fef13..0187788d1d9a58811d659be16ed08b46dfa0e573 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -188,6 +188,8 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
 	mapheaderinfo[num]->typeoflevel = 0;
 	DEH_WriteUndoline("NEXTLEVEL", va("%d", mapheaderinfo[num]->nextlevel), UNDO_NONE);
 	mapheaderinfo[num]->nextlevel = (INT16)(i + 1);
+	DEH_WriteUndoline("KEYWORD", mapheaderinfo[num]->keyword, UNDO_NONE);
+	mapheaderinfo[num]->keyword[0] = '\0';
 	DEH_WriteUndoline("MUSIC", mapheaderinfo[num]->musname, UNDO_NONE);
 	snprintf(mapheaderinfo[num]->musname, 7, "%sM", G_BuildMapName(i));
 	mapheaderinfo[num]->musname[6] = 0;