diff --git a/docs/c-api/mqtt.h/intro.md b/docs/c-api/mqtt.h/intro.md
index 5ae849aa34821e14e1d74073b164380149594c87..bb5d31b43a2d96dcf4aa7f8e51b5ec56566acdb8 100644
--- a/docs/c-api/mqtt.h/intro.md
+++ b/docs/c-api/mqtt.h/intro.md
@@ -5,6 +5,7 @@ decl_name: "mqtt.h"
 items:
   - { name: mg_mqtt_connack.md }
   - { name: mg_mqtt_disconnect.md }
+  - { name: mg_mqtt_match_topic_expression.md }
   - { name: mg_mqtt_next_subscribe_topic.md }
   - { name: mg_mqtt_ping.md }
   - { name: mg_mqtt_pong.md }
@@ -17,6 +18,7 @@ items:
   - { name: mg_mqtt_subscribe.md }
   - { name: mg_mqtt_unsuback.md }
   - { name: mg_mqtt_unsubscribe.md }
+  - { name: mg_mqtt_vmatch_topic_expression.md }
   - { name: mg_send_mqtt_handshake.md }
   - { name: mg_send_mqtt_handshake_opt.md }
   - { name: mg_set_protocol_mqtt.md }
diff --git a/docs/c-api/mqtt.h/mg_mqtt_match_topic_expression.md b/docs/c-api/mqtt.h/mg_mqtt_match_topic_expression.md
new file mode 100644
index 0000000000000000000000000000000000000000..81575b47f5dc597371262e99fa226cbbf1621945
--- /dev/null
+++ b/docs/c-api/mqtt.h/mg_mqtt_match_topic_expression.md
@@ -0,0 +1,12 @@
+---
+title: "mg_mqtt_match_topic_expression()"
+decl_name: "mg_mqtt_match_topic_expression"
+symbol_kind: "func"
+signature: |
+  int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic);
+---
+
+Matches a topic against a topic expression
+
+Returns 1 if it matches; 0 otherwise. 
+
diff --git a/docs/c-api/mqtt.h/mg_mqtt_vmatch_topic_expression.md b/docs/c-api/mqtt.h/mg_mqtt_vmatch_topic_expression.md
new file mode 100644
index 0000000000000000000000000000000000000000..4b0a423157a1fd60d7e8e2cde9b1b28e9036d104
--- /dev/null
+++ b/docs/c-api/mqtt.h/mg_mqtt_vmatch_topic_expression.md
@@ -0,0 +1,11 @@
+---
+title: "mg_mqtt_vmatch_topic_expression()"
+decl_name: "mg_mqtt_vmatch_topic_expression"
+symbol_kind: "func"
+signature: |
+  int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic);
+---
+
+Same as `mg_mqtt_match_topic_expression()`, but takes `exp` as a
+NULL-terminated string. 
+
diff --git a/mongoose.c b/mongoose.c
index 0bda1e2911cb73552b8d42251df7068be4adb0f3..b5ba29a8bbea65ed1d34579c7f817488b31c2f1c 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -9703,6 +9703,21 @@ static void mg_mqtt_proto_data_destructor(void *proto_data) {
   MG_FREE(proto_data);
 }
 
+int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic) {
+  /* TODO(mkm): implement real matching */
+  if (memchr(exp.p, '#', exp.len)) {
+    exp.len -= 2;
+    if (topic.len < exp.len) {
+      exp.len = topic.len;
+    }
+  }
+  return strncmp(topic.p, exp.p, exp.len) == 0;
+}
+
+int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic) {
+  return mg_mqtt_match_topic_expression(mg_mk_str(exp), topic);
+}
+
 void mg_set_protocol_mqtt(struct mg_connection *nc) {
   nc->proto_handler = mqtt_handler;
   nc->proto_data = MG_CALLOC(1, sizeof(struct mg_mqtt_proto_data));
@@ -10040,26 +10055,6 @@ static void mg_mqtt_broker_handle_subscribe(struct mg_connection *nc,
   mg_mqtt_suback(nc, qoss, qoss_len, msg->message_id);
 }
 
-/*
- * Matches a topic against a topic expression
- *
- * See http://goo.gl/iWk21X
- *
- * Returns 1 if it matches; 0 otherwise.
- */
-static int mg_mqtt_match_topic_expression(const char *exp,
-                                          const struct mg_str *topic) {
-  /* TODO(mkm): implement real matching */
-  size_t len = strlen(exp);
-  if (strchr(exp, '#')) {
-    len -= 2;
-    if (topic->len < len) {
-      len = topic->len;
-    }
-  }
-  return strncmp(topic->p, exp, len) == 0;
-}
-
 static void mg_mqtt_broker_handle_publish(struct mg_mqtt_broker *brk,
                                           struct mg_mqtt_message *msg) {
   struct mg_mqtt_session *s;
@@ -10067,8 +10062,8 @@ static void mg_mqtt_broker_handle_publish(struct mg_mqtt_broker *brk,
 
   for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) {
     for (i = 0; i < s->num_subscriptions; i++) {
-      if (mg_mqtt_match_topic_expression(s->subscriptions[i].topic,
-                                         &msg->topic)) {
+      if (mg_mqtt_vmatch_topic_expression(s->subscriptions[i].topic,
+                                          msg->topic)) {
         char buf[100], *p = buf;
         mg_asprintf(&p, sizeof(buf), "%.*s", (int) msg->topic.len,
                     msg->topic.p);
diff --git a/mongoose.h b/mongoose.h
index 1ec11e5126bfee9e44cac6aabb8353e878620697..2933d48ad98d19bd145996bd8861c16444323a5a 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -5152,6 +5152,19 @@ void mg_mqtt_pong(struct mg_connection *nc);
 int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg,
                                  struct mg_str *topic, uint8_t *qos, int pos);
 
+/*
+ * Matches a topic against a topic expression
+ *
+ * Returns 1 if it matches; 0 otherwise.
+ */
+int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic);
+
+/*
+ * Same as `mg_mqtt_match_topic_expression()`, but takes `exp` as a
+ * NULL-terminated string.
+ */
+int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */