diff --git a/caddyconfig/httpcaddyfile/parser_test.go b/caddyconfig/httpcaddyfile/parser_test.go
new file mode 100644
index 000000000..bcecf66e1
--- /dev/null
+++ b/caddyconfig/httpcaddyfile/parser_test.go
@@ -0,0 +1,66 @@
+// Copyright 2015 Matthew Holt and The Caddy Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package httpcaddyfile
+
+import (
+	"testing"
+
+	caddyfile "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
+)
+
+func TestParse(t *testing.T) {
+
+	for i, tc := range []struct {
+		input       string
+		expectWarn  bool
+		expectError bool
+	}{
+		{
+			input: `http://localhost
+			matcher debug {
+			  query showdebug=1
+			}
+			`,
+			expectWarn:  false,
+			expectError: false,
+		},
+		{
+			input: `http://localhost
+			matcher debug {
+			  query bad format
+			}
+			`,
+			expectWarn:  false,
+			expectError: true,
+		},
+	} {
+
+		adapter := caddyfile.Adapter{
+			ServerType: ServerType{},
+		}
+
+		_, warnings, err := adapter.Adapt([]byte(tc.input), nil)
+
+		if len(warnings) > 0 != tc.expectWarn {
+			t.Errorf("Test %d warning expectation failed Expected: %v, got %v", i, tc.expectWarn, warnings)
+			continue
+		}
+
+		if err != nil != tc.expectError {
+			t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
+			continue
+		}
+	}
+}
diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go
index c0e38726f..82fb04a3b 100644
--- a/modules/caddyhttp/matchers.go
+++ b/modules/caddyhttp/matchers.go
@@ -255,8 +255,16 @@ func (MatchQuery) CaddyModule() caddy.ModuleInfo {
 
 // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
 func (m *MatchQuery) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+	if *m == nil {
+		*m = make(map[string][]string)
+	}
+
 	for d.Next() {
-		parts := strings.SplitN(d.Val(), "=", 2)
+		var query string
+		if !d.Args(&query) {
+			return d.ArgErr()
+		}
+		parts := strings.SplitN(query, "=", 2)
 		if len(parts) != 2 {
 			return d.Errf("malformed query matcher token: %s; must be in param=val format", d.Val())
 		}
@@ -268,10 +276,12 @@ func (m *MatchQuery) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 // Match returns true if r matches m.
 func (m MatchQuery) Match(r *http.Request) bool {
 	for param, vals := range m {
-		paramVal := r.URL.Query().Get(param)
-		for _, v := range vals {
-			if paramVal == v {
-				return true
+		paramVal, found := r.URL.Query()[param]
+		if found {
+			for _, v := range vals {
+				if paramVal[0] == v || v == "*" {
+					return true
+				}
 			}
 		}
 	}
diff --git a/modules/caddyhttp/matchers_test.go b/modules/caddyhttp/matchers_test.go
index 9fa7d8e8a..f5ec034f8 100644
--- a/modules/caddyhttp/matchers_test.go
+++ b/modules/caddyhttp/matchers_test.go
@@ -391,6 +391,62 @@ func TestHeaderMatcher(t *testing.T) {
 	}
 }
 
+func TestQueryMatcher(t *testing.T) {
+	for i, tc := range []struct {
+		scenario string
+		match    MatchQuery
+		input    string
+		expect   bool
+	}{
+		{
+			scenario: "non match against a specific value",
+			match:    MatchQuery{"debug": []string{"1"}},
+			input:    "/",
+			expect:   false,
+		},
+		{
+			scenario: "match against a specific value",
+			match:    MatchQuery{"debug": []string{"1"}},
+			input:    "/?debug=1",
+			expect:   true,
+		},
+		{
+			scenario: "match against a wildcard",
+			match:    MatchQuery{"debug": []string{"*"}},
+			input:    "/?debug=something",
+			expect:   true,
+		},
+		{
+			scenario: "non match against a wildcarded",
+			match:    MatchQuery{"debug": []string{"*"}},
+			input:    "/?other=something",
+			expect:   false,
+		},
+		{
+			scenario: "match against an empty value",
+			match:    MatchQuery{"debug": []string{""}},
+			input:    "/?debug",
+			expect:   true,
+		},
+		{
+			scenario: "non match against an empty value",
+			match:    MatchQuery{"debug": []string{""}},
+			input:    "/?someparam",
+			expect:   false,
+		},
+	} {
+
+		u, _ := url.Parse(tc.input)
+
+		req := &http.Request{URL: u}
+		actual := tc.match.Match(req)
+		if actual != tc.expect {
+			t.Errorf("Test %d %v: Expected %t, got %t for '%s'", i, tc.match, tc.expect, actual, tc.input)
+			continue
+		}
+	}
+}
+
 func TestHeaderREMatcher(t *testing.T) {
 	for i, tc := range []struct {
 		match      MatchHeaderRE