diff --git a/httplib.h b/httplib.h
index 65c644e..fb45a95 100644
--- a/httplib.h
+++ b/httplib.h
@@ -3794,6 +3794,7 @@ public:
       switch (state_) {
       case 0: { // Initial boundary
         auto pattern = dash_ + boundary_ + crlf_;
+        buf_erase(buf_find(pattern));
         if (pattern.size() > buf_size()) { return true; }
         if (!buf_start_with(pattern)) { return false; }
         buf_erase(pattern.size());
@@ -3887,17 +3888,13 @@ public:
           if (buf_start_with(pattern)) {
             buf_erase(pattern.size());
             is_valid_ = true;
-            state_ = 5;
+            buf_erase(buf_size()); // Remove epilogue
           } else {
             return true;
           }
         }
         break;
       }
-      case 5: { // Done
-        is_valid_ = false;
-        return false;
-      }
       }
     }
 
diff --git a/test/test.cc b/test/test.cc
index 322b208..3f94e58 100644
--- a/test/test.cc
+++ b/test/test.cc
@@ -3015,8 +3015,10 @@ TEST(GzipDecompressor, ChunkedDecompression) {
     httplib::detail::gzip_compressor compressor;
     bool result = compressor.compress(
         data.data(), data.size(),
-        /*last=*/true, [&](const char *compressed_data_chunk, size_t compressed_data_size) {
-          compressed_data.insert(compressed_data.size(), compressed_data_chunk, compressed_data_size);
+        /*last=*/true,
+        [&](const char *compressed_data_chunk, size_t compressed_data_size) {
+          compressed_data.insert(compressed_data.size(), compressed_data_chunk,
+                                 compressed_data_size);
           return true;
         });
     ASSERT_TRUE(result);
@@ -3035,8 +3037,11 @@ TEST(GzipDecompressor, ChunkedDecompression) {
           std::min(compressed_data.size() - chunk_begin, chunk_size);
       bool result = decompressor.decompress(
           compressed_data.data() + chunk_begin, current_chunk_size,
-          [&](const char *decompressed_data_chunk, size_t decompressed_data_chunk_size) {
-            decompressed_data.insert(decompressed_data.size(), decompressed_data_chunk, decompressed_data_chunk_size);
+          [&](const char *decompressed_data_chunk,
+              size_t decompressed_data_chunk_size) {
+            decompressed_data.insert(decompressed_data.size(),
+                                     decompressed_data_chunk,
+                                     decompressed_data_chunk_size);
             return true;
           });
       ASSERT_TRUE(result);
@@ -4974,5 +4979,48 @@ TEST(MultipartFormDataTest, LargeData) {
   svr.stop();
   t.join();
 }
+
+TEST(MultipartFormDataTest, WithPreamble) {
+  Server svr;
+  svr.Post("/post", [&](const Request &req, Response &res) {
+    res.set_content("ok", "text/plain");
+  });
+
+  thread t = thread([&] { svr.listen(HOST, PORT); });
+  while (!svr.is_running()) {
+    std::this_thread::sleep_for(std::chrono::milliseconds(1));
+  }
+
+  const std::string body =
+      "This is the preamble.  It is to be ignored, though it\r\n"
+      "is a handy place for composition agents to include an\r\n"
+      "explanatory note to non-MIME conformant readers.\r\n"
+      "\r\n"
+      "\r\n"
+      "--simple boundary\r\n"
+      "Content-Disposition: form-data; name=\"field1\"\r\n"
+      "\r\n"
+      "value1\r\n"
+      "--simple boundary\r\n"
+      "Content-Disposition: form-data; name=\"field2\"; "
+      "filename=\"example.txt\"\r\n"
+      "\r\n"
+      "value2\r\n"
+      "--simple boundary--\r\n"
+      "This is the epilogue.  It is also to be ignored.\r\n";
+
+  std::string content_type =
+      R"(multipart/form-data; boundary="simple boundary")";
+
+  Client cli(HOST, PORT);
+  auto res = cli.Post("/post", body, content_type.c_str());
+
+  ASSERT_TRUE(res);
+  EXPECT_EQ(200, res->status);
+
+  svr.stop();
+  t.join();
+}
+
 #endif