diff --git a/Network/Wai/Parse.hs b/Network/Wai/Parse.hs
--- a/Network/Wai/Parse.hs
+++ b/Network/Wai/Parse.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE PatternGuards #-}
 -- | Some helpers for parsing data out of a raw WAI 'Request'.
 
 module Network.Wai.Parse
@@ -17,6 +18,7 @@
     , Param
     , File
     , FileInfo (..)
+    , parseContentType
 #if TEST
     , Bound (..)
     , findBound
@@ -126,24 +128,36 @@
 
 getRequestBodyType :: Request -> Maybe RequestBodyType
 getRequestBodyType req = do
-    ctype <- lookup "Content-Type" $ requestHeaders req
-    if urlenc `S.isPrefixOf` ctype
-        then Just UrlEncoded
-        else case boundary ctype of
-                Just x -> Just $ Multipart x
-                Nothing -> Nothing
+    ctype' <- lookup "Content-Type" $ requestHeaders req
+    let (ctype, attrs) = parseContentType ctype'
+    case ctype of
+        "application/x-www-form-urlencoded" -> return UrlEncoded
+        "multipart/form-data" | Just bound <- lookup "boundary" attrs -> return $ Multipart bound
+        _ -> Nothing
+
+-- | Parse a content type value, turning a single @ByteString@ into the actual
+-- content type and a list of pairs of attributes.
+--
+-- Since 1.3.2
+parseContentType :: S.ByteString -> (S.ByteString, [(S.ByteString, S.ByteString)])
+parseContentType a = do
+    let (ctype, b) = S.break (== semicolon) a
+        attrs = goAttrs id $ S.drop 1 b
+     in (ctype, attrs)
   where
-    urlenc = S8.pack "application/x-www-form-urlencoded"
-    formBound = S8.pack "multipart/form-data;"
-    bound' = "boundary="
-    boundary s =
-        if "multipart/form-data;" `S.isPrefixOf` s
-            then
-                let s' = S.dropWhile (== 32) $ S.drop (S.length formBound) s
-                 in if bound' `S.isPrefixOf` s'
-                        then Just $ S.drop (S.length bound') s'
-                        else Nothing
-            else Nothing
+    semicolon = 59
+    equals = 61
+    space = 32
+    goAttrs front bs
+        | S.null bs = front []
+        | otherwise =
+            let (x, rest) = S.break (== semicolon) bs
+             in goAttrs (front . (goAttr x:)) $ S.drop 1 rest
+    goAttr bs =
+        let (k, v') = S.break (== equals) bs
+            v = S.drop 1 v'
+         in (strip k, strip v)
+    strip = S.dropWhile (== space) . fst . S.breakEnd (/= space)
 
 parseRequestBody :: BackEnd y
                  -> Request
diff --git a/test/WaiExtraTest.hs b/test/WaiExtraTest.hs
--- a/test/WaiExtraTest.hs
+++ b/test/WaiExtraTest.hs
@@ -12,6 +12,7 @@
 import qualified Data.ByteString.Lazy.Char8 as L8
 import qualified Data.ByteString.Lazy as L
 import qualified Data.Text.Lazy as T
+import qualified Data.Text as TS
 import qualified Data.Text.Encoding as TE
 import Control.Arrow
 
@@ -38,11 +39,19 @@
 specs :: Spec
 specs = do
   describe "Network.Wai.Parse" $ do
+    describe "parseContentType" $ do
+        let go (x, y, z) = it (TS.unpack $ TE.decodeUtf8 x) $ parseContentType x `shouldBe` (y, z)
+        mapM_ go
+            [ ("text/plain", "text/plain", [])
+            , ("text/plain; charset=UTF-8 ", "text/plain", [("charset", "UTF-8")])
+            , ("text/plain; charset=UTF-8 ; boundary = foo", "text/plain", [("charset", "UTF-8"), ("boundary", "foo")])
+            ]
     it "parseQueryString" caseParseQueryString
     it "parseQueryString with question mark" caseParseQueryStringQM
     it "parseHttpAccept" caseParseHttpAccept
     it "parseRequestBody" caseParseRequestBody
     it "multipart with plus" caseMultipartPlus
+    it "multipart with multiple attributes" caseMultipartAttrs
     it "urlencoded with plus" caseUrlEncPlus
     {-
     , it "findBound" caseFindBound
@@ -187,6 +196,19 @@
         "has+plus\n" ++
         "--AaB03x--"
     ctype = "multipart/form-data; boundary=AaB03x"
+
+caseMultipartAttrs :: Assertion
+caseMultipartAttrs = do
+    result <- C.runResourceT $ parseRequestBody' lbsBackEnd $ toRequest ctype content
+    liftIO $ result @?= ([("email", "has+plus")], [])
+  where
+    content = S8.pack $
+        "--AaB03x\n" ++
+        "Content-Disposition: form-data; name=\"email\"\n" ++
+        "Content-Type: text/plain; charset=iso-8859-1\n\n" ++
+        "has+plus\n" ++
+        "--AaB03x--"
+    ctype = "multipart/form-data; charset=UTF-8; boundary=AaB03x"
 
 caseUrlEncPlus :: Assertion
 caseUrlEncPlus = do
diff --git a/wai-extra.cabal b/wai-extra.cabal
--- a/wai-extra.cabal
+++ b/wai-extra.cabal
@@ -1,5 +1,5 @@
 Name:                wai-extra
-Version:             1.3.1.1
+Version:             1.3.2
 Synopsis:            Provides some basic WAI handlers and middleware.
 Description:         The goal here is to provide common features without many dependencies.
 License:             MIT
