diff --git a/Text/Markdown.hs b/Text/Markdown.hs
--- a/Text/Markdown.hs
+++ b/Text/Markdown.hs
@@ -13,6 +13,7 @@
     , msLinkNewTab
     , msBlankBeforeBlockquote
     , msBlockFilter
+    , msAddHeadingId
       -- * Newtype
     , Markdown (..)
       -- * Fenced handlers
@@ -28,10 +29,12 @@
 import Text.Markdown.Block
 import Text.Markdown.Types
 import Prelude hiding (sequence, takeWhile)
+import Data.Char (isAlphaNum)
 import Data.Default (Default (..))
-import Data.List (intercalate)
+import Data.List (intercalate, isInfixOf)
 import Data.Text (Text)
 import qualified Data.Text.Lazy as TL
+import Text.Blaze (toValue)
 import Text.Blaze.Html (ToMarkup (..), Html)
 import Text.Blaze.Html.Renderer.Text (renderHtml)
 import Data.Conduit
@@ -130,8 +133,9 @@
     go (BlockCode a b) = msBlockCodeRenderer ms a (id &&& toMarkup $ b)
     go (BlockQuote bs) = H.blockquote $ blocksToHtml bs
     go BlockRule = H.hr
-    go (BlockHeading level h) =
-        wrap level h
+    go (BlockHeading level h)
+        | msAddHeadingId ms = wrap level H.! HA.id (clean h) $ h
+        | otherwise         = wrap level h
       where
        wrap 1 = H.h1
        wrap 2 = H.h2
@@ -139,6 +143,13 @@
        wrap 4 = H.h4
        wrap 5 = H.h5
        wrap _ = H.h6
+
+       isValidChar c = isAlphaNum c || isInfixOf [c] "-_:."
+
+       clean = toValue . TL.filter isValidChar . (TL.replace " " "-") . TL.toLower . renderHtml
+
+
+
     go BlockReference{} = return ()
 
     blocksToHtml bs = runIdentity $ mapM_ yield bs $$ toHtmlB ms =$ CL.fold mappend mempty
diff --git a/Text/Markdown/Types.hs b/Text/Markdown/Types.hs
--- a/Text/Markdown/Types.hs
+++ b/Text/Markdown/Types.hs
@@ -88,6 +88,19 @@
       -- Default: @id@
       --
       -- Since 0.1.7
+
+    , msAddHeadingId :: Bool
+      -- ^ If @True@, an @id@ attribute is added to the heading tag with the value equal to
+      -- the text with only valid CSS identifier characters.
+      --
+      -- > ## Executive Summary
+      --
+      -- > <h2 id="executive-summary">Executive Summary</h2>
+      --
+      -- Default: @False@
+      --
+      -- Since 0.1.13
+
     }
 
 -- | See 'msFencedHandlers.
@@ -110,6 +123,7 @@
         , msLinkNewTab = False
         , msBlankBeforeBlockquote = True
         , msBlockFilter = id
+        , msAddHeadingId = False
         }
 
 -- | Helper for creating a 'FHRaw'.
diff --git a/markdown.cabal b/markdown.cabal
--- a/markdown.cabal
+++ b/markdown.cabal
@@ -1,5 +1,5 @@
 Name:                markdown
-Version:             0.1.12
+Version:             0.1.13
 Synopsis:            Convert Markdown to HTML, with XSS protection
 Description:         This library leverages existing high-performance libraries (attoparsec, blaze-html, text, and conduit), and should integrate well with existing codebases.
 Homepage:            https://github.com/snoyberg/markdown
@@ -21,6 +21,7 @@
                        Text.Markdown.Inline
   other-modules:       Text.Markdown.Types
   Build-depends:       base                   >= 4       && < 5
+                     , blaze-markup           >= 0.6
                      , blaze-html             >= 0.4
                      , attoparsec             >= 0.10
                      , transformers           >= 0.2.2
diff --git a/test/main.hs b/test/main.hs
--- a/test/main.hs
+++ b/test/main.hs
@@ -146,6 +146,20 @@
             $ check
                 "<h1>foo</h1><h2>bar</h2>"
                 "foo\n=============\n\nbar\n----------------\n"
+    describe "headings with ID" $ do
+        let withHeadingId = def { msAddHeadingId = True }
+        it "without spaces"
+            $ checkSet withHeadingId
+                "<h1 id=\"foo\">foo</h1><h2 id=\"bar\">bar</h2><h3 id=\"baz\">baz</h3>"
+                "# foo\n\n##     bar\n\n###baz"
+        it "with spaces"
+            $ checkSet withHeadingId
+                "<h1 id=\"executive-summary\">Executive summary</h1>"
+                "# Executive summary"
+        it "with special characters"
+            $ checkSet withHeadingId
+                "<h1 id=\"executive-summary-.-_:\">Executive summary .!@#$%^*()-_=:</h1>"
+                "# Executive summary .!@#$%^*()-_=:"
     describe "blockquotes" $ do
         it "simple"
             $ check
@@ -181,7 +195,7 @@
     -}
 
     describe "images" $ do
-        it "simple" $ check 
+        it "simple" $ check
             "<p><img src=\"http://link.to/image.jpg\" alt=\"foo\"></p>"
             "![foo](http://link.to/image.jpg)"
         it "title" $ check
