diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.7.1.0
+
+- `StripBlocks` and `LTrimBlocks` options
+- `+` tag modifier to override whitespace stripping
+
 ## 0.7.0.0
 
 - `keepTrailingNewlines` option
diff --git a/ginger.cabal b/ginger.cabal
--- a/ginger.cabal
+++ b/ginger.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                ginger
-version:             0.7.0.0
+version:             0.7.1.0
 synopsis:            An implementation of the Jinja2 template language in Haskell
 description:         Ginger is Jinja, minus the most blatant pythonisms. Wants
                      to be feature complete, but isn't quite there yet.
diff --git a/src/Text/Ginger/Parse.hs b/src/Text/Ginger/Parse.hs
--- a/src/Text/Ginger/Parse.hs
+++ b/src/Text/Ginger/Parse.hs
@@ -196,6 +196,8 @@
         { poIncludeResolver :: IncludeResolver m
         , poSourceName :: Maybe SourceName
         , poKeepTrailingNewline :: Bool
+        , poLStripBlocks :: Bool
+        , poTrimBlocks :: Bool
         }
 
 mkParserOptions :: Monad m => IncludeResolver m -> ParserOptions m
@@ -204,6 +206,8 @@
         { poIncludeResolver = resolver
         , poSourceName = Nothing
         , poKeepTrailingNewline = False
+        , poLStripBlocks = False
+        , poTrimBlocks = False
         }
 
 data ParseState
@@ -224,6 +228,21 @@
 ignore :: Monad m => m a -> m ()
 ignore = (>> return ())
 
+ifFlag :: Monad m => (ParserOptions m -> Bool) -> Parser m () -> Parser m () -> Parser m ()
+ifFlag flag yes no = do
+    cond <- asks flag
+    if cond then yes else no
+
+whenFlag :: Monad m => (ParserOptions m -> Bool) -> Parser m () -> Parser m ()
+whenFlag flag yes = do
+    cond <- asks flag
+    when cond yes
+
+unlessFlag :: Monad m => (ParserOptions m -> Bool) -> Parser m () -> Parser m ()
+unlessFlag flag no = do
+    cond <- asks flag
+    when (not cond) no
+
 getResolver :: Monad m => Parser m (IncludeResolver m)
 getResolver = asks poIncludeResolver
 
@@ -240,7 +259,8 @@
     resolver <- getResolver
     currentSource <- fromMaybe "" <$> asks poSourceName
     let includeSourceName = takeDirectory currentSource </> sourceName
-    pres <- lift . lift $ parseGingerFile resolver includeSourceName
+    opts <- ask
+    pres <- lift . lift $ parseGingerFile' opts includeSourceName
     case pres of
         Right t -> return t
         Left err -> fail (show err)
@@ -860,13 +880,14 @@
 closeTagP :: Monad m => Parser m ()
 closeTagP = do
     closeP '%'
-    keepTrailingNewline <- asks poKeepTrailingNewline
-    when
-        (not keepTrailingNewline)
+
+    unlessFlag poKeepTrailingNewline
         (ignore . optional $ literalNewlineP)
 
 openP :: Monad m => Char -> Parser m ()
-openP c = try (openWP c) <|> try (openNWP c)
+openP c = try (openWP c)
+        <|> try (openFWP c)
+        <|> try (openNWP c)
 
 openWP :: Monad m => Char -> Parser m ()
 openWP c = ignore $ do
@@ -874,13 +895,23 @@
     string [ '{', c, '-' ]
     spacesOrComment
 
+openFWP :: Monad m => Char -> Parser m ()
+openFWP c = ignore $ do
+    string [ '{', c, '+' ]
+    spacesOrComment
+
+
 openNWP :: Monad m => Char -> Parser m ()
 openNWP c = ignore $ do
+    whenFlag poLStripBlocks spaces
     string [ '{', c ]
+    notFollowedBy $ oneOf "+-"
     spacesOrComment
 
 closeP :: Monad m => Char -> Parser m ()
-closeP c = try (closeWP c) <|> try (closeNWP c)
+closeP c = try (closeWP c)
+         <|> try (closeFWP c)
+         <|> try (closeNWP c)
 
 closeWP :: Monad m => Char -> Parser m ()
 closeWP c = ignore $ do
@@ -888,10 +919,16 @@
     string [ '-', c, '}' ]
     spaces
 
+closeFWP :: Monad m => Char -> Parser m ()
+closeFWP c = ignore $ do
+    spacesOrComment
+    string [ '+', c, '}' ]
+
 closeNWP :: Monad m => Char -> Parser m ()
 closeNWP c = ignore $ do
     spacesOrComment
     string [ c, '}' ]
+    whenFlag poTrimBlocks spaces
 
 expressionP :: Monad m => Parser m (Expression SourcePos)
 expressionP = lambdaExprP <|> ternaryExprP
diff --git a/test/Text/Ginger/SimulationTests.hs b/test/Text/Ginger/SimulationTests.hs
--- a/test/Text/Ginger/SimulationTests.hs
+++ b/test/Text/Ginger/SimulationTests.hs
@@ -29,7 +29,7 @@
         [ testCase "Comment does not appear in output" $ mkTestHtml
             [] [] "- {# Comments #} -" "-  -"
         ]
-    , testGroup "Dashed limiters eat whitespace"
+    , testGroup "Dashed delimiters eat whitespace"
         [ testCase "comments" $ mkTestHtml
             [] [] "- {#- Comment -#} -" "--"
         , testCase "interpolations" $ mkTestHtml
@@ -37,6 +37,45 @@
         , testCase "flow" $ mkTestHtml
             [] [] "- {%- set x=1 -%} -" "--"
         ]
+    , testGroup "Default delimiters eat whitespace with LStripBlocks"
+        [ testCase "comments" $ mkTestHtmlOpts
+            (\o -> o { poLStripBlocks = True })
+            [] [] "- {# Comment #}" "-"
+        , testCase "interpolations" $ mkTestHtmlOpts
+            (\o -> o { poLStripBlocks = True })
+            [] [] "- {{ '' }}" "-"
+        , testCase "flow" $ mkTestHtmlOpts
+            (\o -> o { poLStripBlocks = True })
+            [] [] "- {% set x=1 %}" "-"
+        , testCase "interpolations, left-only" $ mkTestHtmlOpts
+            (\o -> o { poLStripBlocks = True })
+            [] [] "- {{ 'a' }} -" "-a -"
+        ]
+    , testGroup "Default delimiters eat whitespace with TrimBlocks"
+        [ testCase "comments" $ mkTestHtmlOpts
+            (\o -> o { poTrimBlocks = True })
+            [] [] "{# Comment #} -" "-"
+        , testCase "interpolations" $ mkTestHtmlOpts
+            (\o -> o { poTrimBlocks = True })
+            [] [] "{{ '' }} -" "-"
+        , testCase "flow" $ mkTestHtmlOpts
+            (\o -> o { poTrimBlocks = True })
+            [] [] "{% set x=1 %} -" "-"
+        , testCase "interpolations, right-only" $ mkTestHtmlOpts
+            (\o -> o { poTrimBlocks = True })
+            [] [] "- {{ 'a' }} -" "- a-"
+        ]
+    , testGroup "Plussed delimiters override LStrip/TrimBlocks"
+        [ testCase "comments" $ mkTestHtmlOpts
+            (\o -> o { poTrimBlocks = True, poLStripBlocks = True })
+            [] [] "- {#+ Comment +#} -" "-  -"
+        , testCase "interpolations" $ mkTestHtmlOpts
+            (\o -> o { poTrimBlocks = True, poLStripBlocks = True })
+            [] [] "- {{+ '' +}} -" "-  -"
+        , testCase "flow" $ mkTestHtmlOpts
+            (\o -> o { poTrimBlocks = True, poLStripBlocks = True })
+            [] [] "- {%+ set x=1 +%} -" "-  -"
+        ]
     , testGroup "Trailing newlines"
         [ testCase "eaten after blocks" $ mkTestHtml
             [] [] "{% if true %}\nHello!\n{% endif %}\n" "Hello!\n"
@@ -782,6 +821,11 @@
             mkTestHtml [] [("./features-included.html", "Hello, {{ user }}!")]
                 "{% set user='world' %}{% include 'features-included.html' %}"
                 "Hello, world!"
+        , testCase "not eaten after blocks in included template when keepTrailingNewline is on" $
+            mkTestHtmlOpts (\o -> o { poKeepTrailingNewline = True })
+                [] [("./features-included.html", "Hello, {% if true %}\n{{ user }}{% endif %}\n!")]
+                "{% set user='world' %}{% include 'features-included.html' %}"
+                "Hello, \nworld\n!"
         , testCase "include referencing an included variable" $ do
             mkTestHtml [] [("./features-included.html", "{% set user = 'foobar' %}")]
                 "{% include 'features-included.html' %}Hello, {{ user }}!"
