diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs
index 866fca764..7c86beac3 100644
--- a/askama_derive/src/generator.rs
+++ b/askama_derive/src/generator.rs
@@ -12,7 +12,7 @@ use parser::node::{
     Call, Comment, CondTest, FilterBlock, If, Include, Let, Lit, Loop, Match, Target, Whitespace,
     Ws,
 };
-use parser::{Expr, Node};
+use parser::{Expr, Filter, Node};
 use quote::quote;
 
 pub(crate) struct Generator<'a> {
@@ -736,10 +736,15 @@ impl<'a> Generator<'a> {
         mem::drop(mem::replace(&mut self.buf_writable, current_buf));
 
         let mut filter_buf = Buffer::new(buf.indent);
-        let mut args = filter.args.clone();
-        args.insert(0, Expr::Generated(var_name.clone()));
+        let Filter {
+            name: filter_name,
+            arguments,
+        } = &filter.filters;
+        let mut arguments = arguments.clone();
 
-        let wrap = self.visit_filter(&mut filter_buf, filter.filter_name, &args)?;
+        insert_variable_at_the_end(&mut arguments, var_name.clone());
+
+        let wrap = self.visit_filter(&mut filter_buf, filter_name, &arguments)?;
 
         self.buf_writable
             .push(Writable::Generated(filter_buf.buf, wrap));
@@ -1140,7 +1145,10 @@ impl<'a> Generator<'a> {
             Expr::Array(ref elements) => self.visit_array(buf, elements)?,
             Expr::Attr(ref obj, name) => self.visit_attr(buf, obj, name)?,
             Expr::Index(ref obj, ref key) => self.visit_index(buf, obj, key)?,
-            Expr::Filter(name, ref args) => self.visit_filter(buf, name, args)?,
+            Expr::Filter(Filter {
+                name,
+                ref arguments,
+            }) => self.visit_filter(buf, name, arguments)?,
             Expr::Unary(op, ref inner) => self.visit_unary(buf, op, inner)?,
             Expr::BinOp(op, ref left, ref right) => self.visit_binop(buf, op, left, right)?,
             Expr::Range(op, ref left, ref right) => {
@@ -2003,7 +2011,7 @@ pub(crate) fn is_cacheable(expr: &Expr<'_>) -> bool {
         Expr::Array(args) => args.iter().all(is_cacheable),
         Expr::Attr(lhs, _) => is_cacheable(lhs),
         Expr::Index(lhs, rhs) => is_cacheable(lhs) && is_cacheable(rhs),
-        Expr::Filter(_, args) => args.iter().all(is_cacheable),
+        Expr::Filter(Filter { arguments, .. }) => arguments.iter().all(is_cacheable),
         Expr::Unary(_, arg) => is_cacheable(arg),
         Expr::BinOp(_, lhs, rhs) => is_cacheable(lhs) && is_cacheable(rhs),
         Expr::Range(_, lhs, rhs) => {
@@ -2030,6 +2038,14 @@ fn median(sizes: &mut [usize]) -> usize {
     }
 }
 
+fn insert_variable_at_the_end(args: &mut Vec<Expr<'_>>, var_name: String) {
+    if let Some(Expr::Filter(Filter { arguments, .. })) = args.first_mut() {
+        insert_variable_at_the_end(arguments, var_name);
+    } else {
+        args.insert(0, Expr::Generated(var_name));
+    }
+}
+
 #[derive(Clone, Copy, PartialEq)]
 enum AstLevel {
     Top,
diff --git a/askama_parser/src/expr.rs b/askama_parser/src/expr.rs
index be5adfd2f..dddfb52da 100644
--- a/askama_parser/src/expr.rs
+++ b/askama_parser/src/expr.rs
@@ -12,7 +12,8 @@ use nom::multi::{fold_many0, many0, separated_list0};
 use nom::sequence::{pair, preceded, terminated, tuple};
 
 use super::{
-    char_lit, identifier, not_ws, num_lit, path_or_identifier, str_lit, ws, Level, PathOrIdentifier,
+    char_lit, filter, identifier, not_ws, num_lit, path_or_identifier, str_lit, ws, Level,
+    PathOrIdentifier,
 };
 use crate::{ErrorContext, ParseResult};
 
@@ -62,7 +63,7 @@ pub enum Expr<'a> {
     Array(Vec<Expr<'a>>),
     Attr(Box<Expr<'a>>, &'a str),
     Index(Box<Expr<'a>>, Box<Expr<'a>>),
-    Filter(&'a str, Vec<Expr<'a>>),
+    Filter(Filter<'a>),
     NamedArgument(&'a str, Box<Expr<'a>>),
     Unary(&'a str, Box<Expr<'a>>),
     BinOp(&'a str, Box<Expr<'a>>, Box<Expr<'a>>),
@@ -188,28 +189,21 @@ impl<'a> Expr<'a> {
 
     fn filtered(i: &'a str, level: Level) -> ParseResult<'a, Self> {
         let (_, level) = level.nest(i)?;
-        #[allow(clippy::type_complexity)]
-        fn filter(i: &str, level: Level) -> ParseResult<'_, (&str, Option<Vec<Expr<'_>>>)> {
-            let (i, (_, fname, args)) = tuple((
-                char('|'),
-                ws(identifier),
-                opt(|i| Expr::arguments(i, level, false)),
-            ))(i)?;
-            Ok((i, (fname, args)))
-        }
-
         let (i, (obj, filters)) =
             tuple((|i| Self::prefix(i, level), many0(|i| filter(i, level))))(i)?;
 
         let mut res = obj;
         for (fname, args) in filters {
-            res = Self::Filter(fname, {
-                let mut args = match args {
-                    Some(inner) => inner,
-                    None => Vec::new(),
-                };
-                args.insert(0, res);
-                args
+            res = Self::Filter(Filter {
+                name: fname,
+                arguments: {
+                    let mut args = match args {
+                        Some(inner) => inner,
+                        None => Vec::new(),
+                    };
+                    args.insert(0, res);
+                    args
+                },
             });
         }
 
@@ -310,6 +304,12 @@ impl<'a> Expr<'a> {
     }
 }
 
+#[derive(Clone, Debug, PartialEq)]
+pub struct Filter<'a> {
+    pub name: &'a str,
+    pub arguments: Vec<Expr<'a>>,
+}
+
 enum Suffix<'a> {
     Attr(&'a str),
     Index(Expr<'a>),
diff --git a/askama_parser/src/lib.rs b/askama_parser/src/lib.rs
index 3f93a370e..9d6c9acf2 100644
--- a/askama_parser/src/lib.rs
+++ b/askama_parser/src/lib.rs
@@ -15,7 +15,7 @@ use nom::sequence::{delimited, pair, preceded, terminated, tuple};
 use nom::{error_position, AsChar, InputTakeAtPosition};
 
 pub mod expr;
-pub use expr::Expr;
+pub use expr::{Expr, Filter};
 pub mod node;
 pub use node::Node;
 #[cfg(test)]
@@ -477,3 +477,13 @@ impl Level {
 
     const MAX_DEPTH: u8 = 128;
 }
+
+#[allow(clippy::type_complexity)]
+fn filter(i: &str, level: Level) -> ParseResult<'_, (&str, Option<Vec<Expr<'_>>>)> {
+    let (i, (_, fname, args)) = tuple((
+        char('|'),
+        ws(identifier),
+        opt(|i| Expr::arguments(i, level, false)),
+    ))(i)?;
+    Ok((i, (fname, args)))
+}
diff --git a/askama_parser/src/node.rs b/askama_parser/src/node.rs
index a50c2e7a8..fdfc6e8b2 100644
--- a/askama_parser/src/node.rs
+++ b/askama_parser/src/node.rs
@@ -15,8 +15,8 @@ use nom::sequence::{delimited, pair, preceded, terminated, tuple};
 use crate::{ErrorContext, ParseResult};
 
 use super::{
-    bool_lit, char_lit, identifier, is_ws, keyword, num_lit, path_or_identifier, skip_till,
-    str_lit, ws, Expr, PathOrIdentifier, State,
+    bool_lit, char_lit, filter, identifier, is_ws, keyword, num_lit, path_or_identifier, skip_till,
+    str_lit, ws, Expr, Filter, PathOrIdentifier, State,
 };
 
 #[derive(Debug, PartialEq)]
@@ -563,8 +563,7 @@ impl<'a> Macro<'a> {
 #[derive(Debug, PartialEq)]
 pub struct FilterBlock<'a> {
     pub ws1: Ws,
-    pub filter_name: &'a str,
-    pub args: Vec<Expr<'a>>,
+    pub filters: Filter<'a>,
     pub nodes: Vec<Node<'a>>,
     pub ws2: Ws,
 }
@@ -577,11 +576,27 @@ impl<'a> FilterBlock<'a> {
             cut(tuple((
                 ws(identifier),
                 opt(|i| Expr::arguments(i, s.level.get(), false)),
+                many0(|i| filter(i, s.level.get())),
                 opt(Whitespace::parse),
                 |i| s.tag_block_end(i),
             ))),
         ));
-        let (i, (pws1, _, (filter_name, params, nws1, _))) = start(i)?;
+        let (i, (pws1, _, (filter_name, params, extra_filters, nws1, _))) = start(i)?;
+
+        let mut filters = Filter {
+            name: filter_name,
+            arguments: params.unwrap_or_default(),
+        };
+        for (filter_name, args) in extra_filters {
+            filters = Filter {
+                name: filter_name,
+                arguments: {
+                    let mut args = args.unwrap_or_default();
+                    args.insert(0, Expr::Filter(filters));
+                    args
+                },
+            };
+        }
 
         let mut end = cut(tuple((
             |i| Node::many(i, s),
@@ -598,8 +613,7 @@ impl<'a> FilterBlock<'a> {
             i,
             Self {
                 ws1: Ws(pws1, nws1),
-                filter_name,
-                args: params.unwrap_or_default(),
+                filters,
                 nodes,
                 ws2: Ws(pws2, nws2),
             },
diff --git a/askama_parser/src/tests.rs b/askama_parser/src/tests.rs
index 742f0f389..669e376d1 100644
--- a/askama_parser/src/tests.rs
+++ b/askama_parser/src/tests.rs
@@ -1,5 +1,5 @@
 use super::node::{Lit, Whitespace, Ws};
-use super::{Ast, Expr, Node, Syntax};
+use super::{Ast, Expr, Filter, Node, Syntax};
 
 fn check_ws_split(s: &str, res: &(&str, &str, &str)) {
     let Lit { lws, val, rws } = Lit::split_ws_parts(s);
@@ -25,33 +25,47 @@ fn test_invalid_block() {
 
 #[test]
 fn test_parse_filter() {
-    use Expr::*;
     let syntax = Syntax::default();
     assert_eq!(
         Ast::from_str("{{ strvar|e }}", &syntax).unwrap().nodes,
-        vec![Node::Expr(Ws(None, None), Filter("e", vec![Var("strvar")]),)],
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Filter(Filter {
+                name: "e",
+                arguments: vec![Expr::Var("strvar")]
+            }),
+        )],
     );
     assert_eq!(
         Ast::from_str("{{ 2|abs }}", &syntax).unwrap().nodes,
-        vec![Node::Expr(Ws(None, None), Filter("abs", vec![NumLit("2")]),)],
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Filter(Filter {
+                name: "abs",
+                arguments: vec![Expr::NumLit("2")]
+            }),
+        )],
     );
     assert_eq!(
         Ast::from_str("{{ -2|abs }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Filter("abs", vec![Unary("-", NumLit("2").into())]),
+            Expr::Filter(Filter {
+                name: "abs",
+                arguments: vec![Expr::Unary("-", Expr::NumLit("2").into())]
+            }),
         )],
     );
     assert_eq!(
         Ast::from_str("{{ (1 - 2)|abs }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Filter(
-                "abs",
-                vec![Group(
-                    BinOp("-", NumLit("1").into(), NumLit("2").into()).into()
-                )]
-            ),
+            Expr::Filter(Filter {
+                name: "abs",
+                arguments: vec![Expr::Group(
+                    Expr::BinOp("-", Expr::NumLit("1").into(), Expr::NumLit("2").into()).into()
+                )],
+            },),
         )],
     );
 }
@@ -283,16 +297,15 @@ fn change_delimiters_parse_filter() {
 
 #[test]
 fn test_precedence() {
-    use Expr::*;
     let syntax = Syntax::default();
     assert_eq!(
         Ast::from_str("{{ a + b == c }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "==",
-                BinOp("+", Var("a").into(), Var("b").into()).into(),
-                Var("c").into(),
+                Expr::BinOp("+", Expr::Var("a").into(), Expr::Var("b").into()).into(),
+                Expr::Var("c").into(),
             )
         )],
     );
@@ -302,15 +315,15 @@ fn test_precedence() {
             .nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "-",
-                BinOp(
+                Expr::BinOp(
                     "+",
-                    Var("a").into(),
-                    BinOp("*", Var("b").into(), Var("c").into()).into(),
+                    Expr::Var("a").into(),
+                    Expr::BinOp("*", Expr::Var("b").into(), Expr::Var("c").into()).into(),
                 )
                 .into(),
-                BinOp("/", Var("d").into(), Var("e").into()).into(),
+                Expr::BinOp("/", Expr::Var("d").into(), Expr::Var("e").into()).into(),
             )
         )],
     );
@@ -320,15 +333,18 @@ fn test_precedence() {
             .nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "/",
-                BinOp(
+                Expr::BinOp(
                     "*",
-                    Var("a").into(),
-                    Group(BinOp("+", Var("b").into(), Var("c").into()).into()).into()
+                    Expr::Var("a").into(),
+                    Expr::Group(
+                        Expr::BinOp("+", Expr::Var("b").into(), Expr::Var("c").into()).into()
+                    )
+                    .into()
                 )
                 .into(),
-                Unary("-", Var("d").into()).into()
+                Expr::Unary("-", Expr::Var("d").into()).into()
             )
         )],
     );
@@ -338,15 +354,15 @@ fn test_precedence() {
             .nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "||",
-                BinOp(
+                Expr::BinOp(
                     "||",
-                    Var("a").into(),
-                    BinOp("&&", Var("b").into(), Var("c").into()).into(),
+                    Expr::Var("a").into(),
+                    Expr::BinOp("&&", Expr::Var("b").into(), Expr::Var("c").into()).into(),
                 )
                 .into(),
-                BinOp("&&", Var("d").into(), Var("e").into()).into(),
+                Expr::BinOp("&&", Expr::Var("d").into(), Expr::Var("e").into()).into(),
             )
         )],
     );
@@ -354,16 +370,15 @@ fn test_precedence() {
 
 #[test]
 fn test_associativity() {
-    use Expr::*;
     let syntax = Syntax::default();
     assert_eq!(
         Ast::from_str("{{ a + b + c }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "+",
-                BinOp("+", Var("a").into(), Var("b").into()).into(),
-                Var("c").into()
+                Expr::BinOp("+", Expr::Var("a").into(), Expr::Var("b").into()).into(),
+                Expr::Var("c").into()
             )
         )],
     );
@@ -371,10 +386,10 @@ fn test_associativity() {
         Ast::from_str("{{ a * b * c }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "*",
-                BinOp("*", Var("a").into(), Var("b").into()).into(),
-                Var("c").into()
+                Expr::BinOp("*", Expr::Var("a").into(), Expr::Var("b").into()).into(),
+                Expr::Var("c").into()
             )
         )],
     );
@@ -382,10 +397,10 @@ fn test_associativity() {
         Ast::from_str("{{ a && b && c }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "&&",
-                BinOp("&&", Var("a").into(), Var("b").into()).into(),
-                Var("c").into()
+                Expr::BinOp("&&", Expr::Var("a").into(), Expr::Var("b").into()).into(),
+                Expr::Var("c").into()
             )
         )],
     );
@@ -393,15 +408,15 @@ fn test_associativity() {
         Ast::from_str("{{ a + b - c + d }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "+",
-                BinOp(
+                Expr::BinOp(
                     "-",
-                    BinOp("+", Var("a").into(), Var("b").into()).into(),
-                    Var("c").into()
+                    Expr::BinOp("+", Expr::Var("a").into(), Expr::Var("b").into()).into(),
+                    Expr::Var("c").into()
                 )
                 .into(),
-                Var("d").into()
+                Expr::Var("d").into()
             )
         )],
     );
@@ -411,25 +426,25 @@ fn test_associativity() {
             .nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "==",
-                BinOp(
+                Expr::BinOp(
                     ">",
-                    BinOp(
+                    Expr::BinOp(
                         ">",
-                        BinOp(
+                        Expr::BinOp(
                             "!=",
-                            BinOp("==", Var("a").into(), Var("b").into()).into(),
-                            Var("c").into()
+                            Expr::BinOp("==", Expr::Var("a").into(), Expr::Var("b").into()).into(),
+                            Expr::Var("c").into()
                         )
                         .into(),
-                        Var("d").into()
+                        Expr::Var("d").into()
                     )
                     .into(),
-                    Var("e").into()
+                    Expr::Var("e").into()
                 )
                 .into(),
-                Var("f").into()
+                Expr::Var("f").into()
             )
         )],
     );
@@ -437,15 +452,17 @@ fn test_associativity() {
 
 #[test]
 fn test_odd_calls() {
-    use Expr::*;
     let syntax = Syntax::default();
     assert_eq!(
         Ast::from_str("{{ a[b](c) }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Call(
-                Box::new(Index(Box::new(Var("a")), Box::new(Var("b")))),
-                vec![Var("c")],
+            Expr::Call(
+                Box::new(Expr::Index(
+                    Box::new(Expr::Var("a")),
+                    Box::new(Expr::Var("b"))
+                )),
+                vec![Expr::Var("c")],
             ),
         )],
     );
@@ -453,13 +470,13 @@ fn test_odd_calls() {
         Ast::from_str("{{ (a + b)(c) }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Call(
-                Box::new(Group(Box::new(BinOp(
+            Expr::Call(
+                Box::new(Expr::Group(Box::new(Expr::BinOp(
                     "+",
-                    Box::new(Var("a")),
-                    Box::new(Var("b"))
+                    Box::new(Expr::Var("a")),
+                    Box::new(Expr::Var("b"))
                 )))),
-                vec![Var("c")],
+                vec![Expr::Var("c")],
             ),
         )],
     );
@@ -467,10 +484,10 @@ fn test_odd_calls() {
         Ast::from_str("{{ a + b(c) }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "+",
-                Box::new(Var("a")),
-                Box::new(Call(Box::new(Var("b")), vec![Var("c")])),
+                Box::new(Expr::Var("a")),
+                Box::new(Expr::Call(Box::new(Expr::Var("b")), vec![Expr::Var("c")])),
             ),
         )],
     );
@@ -478,9 +495,12 @@ fn test_odd_calls() {
         Ast::from_str("{{ (-a)(b) }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Call(
-                Box::new(Group(Box::new(Unary("-", Box::new(Var("a")))))),
-                vec![Var("b")],
+            Expr::Call(
+                Box::new(Expr::Group(Box::new(Expr::Unary(
+                    "-",
+                    Box::new(Expr::Var("a"))
+                )))),
+                vec![Expr::Var("b")],
             ),
         )],
     );
@@ -488,31 +508,40 @@ fn test_odd_calls() {
         Ast::from_str("{{ -a(b) }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Unary("-", Box::new(Call(Box::new(Var("a")), vec![Var("b")]))),
+            Expr::Unary(
+                "-",
+                Box::new(Expr::Call(Box::new(Expr::Var("a")), vec![Expr::Var("b")]))
+            ),
         )],
     );
     assert_eq!(
         Ast::from_str("{{ a(b)|c }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Filter("c", vec![Call(Box::new(Var("a")), vec![Var("b")])]),
+            Expr::Filter(Filter {
+                name: "c",
+                arguments: vec![Expr::Call(Box::new(Expr::Var("a")), vec![Expr::Var("b")])]
+            }),
         )]
     );
     assert_eq!(
         Ast::from_str("{{ a(b)| c }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Filter("c", vec![Call(Box::new(Var("a")), vec![Var("b")])]),
+            Expr::Filter(Filter {
+                name: "c",
+                arguments: vec![Expr::Call(Box::new(Expr::Var("a")), vec![Expr::Var("b")])]
+            }),
         )]
     );
     assert_eq!(
         Ast::from_str("{{ a(b) |c }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "|",
-                Box::new(Call(Box::new(Var("a")), vec![Var("b")])),
-                Box::new(Var("c"))
+                Box::new(Expr::Call(Box::new(Expr::Var("a")), vec![Expr::Var("b")])),
+                Box::new(Expr::Var("c"))
             ),
         )]
     );
@@ -579,82 +608,110 @@ fn test_parse_comments() {
 
 #[test]
 fn test_parse_tuple() {
-    use super::expr::Expr::*;
     let syntax = Syntax::default();
     assert_eq!(
         Ast::from_str("{{ () }}", &syntax).unwrap().nodes,
-        vec![Node::Expr(Ws(None, None), Tuple(vec![]),)],
+        vec![Node::Expr(Ws(None, None), Expr::Tuple(vec![]),)],
     );
     assert_eq!(
         Ast::from_str("{{ (1) }}", &syntax).unwrap().nodes,
-        vec![Node::Expr(Ws(None, None), Group(Box::new(NumLit("1"))),)],
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Group(Box::new(Expr::NumLit("1"))),
+        )],
     );
     assert_eq!(
         Ast::from_str("{{ (1,) }}", &syntax).unwrap().nodes,
-        vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)],
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Tuple(vec![Expr::NumLit("1")]),
+        )],
     );
     assert_eq!(
         Ast::from_str("{{ (1, ) }}", &syntax).unwrap().nodes,
-        vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)],
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Tuple(vec![Expr::NumLit("1")]),
+        )],
     );
     assert_eq!(
         Ast::from_str("{{ (1 ,) }}", &syntax).unwrap().nodes,
-        vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)],
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Tuple(vec![Expr::NumLit("1")]),
+        )],
     );
     assert_eq!(
         Ast::from_str("{{ (1 , ) }}", &syntax).unwrap().nodes,
-        vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)],
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Tuple(vec![Expr::NumLit("1")]),
+        )],
     );
     assert_eq!(
         Ast::from_str("{{ (1, 2) }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Tuple(vec![NumLit("1"), NumLit("2")]),
+            Expr::Tuple(vec![Expr::NumLit("1"), Expr::NumLit("2")]),
         )],
     );
     assert_eq!(
         Ast::from_str("{{ (1, 2,) }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Tuple(vec![NumLit("1"), NumLit("2")]),
+            Expr::Tuple(vec![Expr::NumLit("1"), Expr::NumLit("2")]),
         )],
     );
     assert_eq!(
         Ast::from_str("{{ (1, 2, 3) }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Tuple(vec![NumLit("1"), NumLit("2"), NumLit("3")]),
+            Expr::Tuple(vec![
+                Expr::NumLit("1"),
+                Expr::NumLit("2"),
+                Expr::NumLit("3")
+            ]),
         )],
     );
     assert_eq!(
         Ast::from_str("{{ ()|abs }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Filter("abs", vec![Tuple(vec![])]),
+            Expr::Filter(Filter {
+                name: "abs",
+                arguments: vec![Expr::Tuple(vec![])]
+            }),
         )],
     );
     assert_eq!(
         Ast::from_str("{{ () | abs }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp("|", Box::new(Tuple(vec![])), Box::new(Var("abs"))),
+            Expr::BinOp(
+                "|",
+                Box::new(Expr::Tuple(vec![])),
+                Box::new(Expr::Var("abs"))
+            ),
         )],
     );
     assert_eq!(
         Ast::from_str("{{ (1)|abs }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Filter("abs", vec![Group(Box::new(NumLit("1")))]),
+            Expr::Filter(Filter {
+                name: "abs",
+                arguments: vec![Expr::Group(Box::new(Expr::NumLit("1")))]
+            }),
         )],
     );
     assert_eq!(
         Ast::from_str("{{ (1) | abs }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "|",
-                Box::new(Group(Box::new(NumLit("1")))),
-                Box::new(Var("abs"))
+                Box::new(Expr::Group(Box::new(Expr::NumLit("1")))),
+                Box::new(Expr::Var("abs"))
             ),
         )],
     );
@@ -662,17 +719,20 @@ fn test_parse_tuple() {
         Ast::from_str("{{ (1,)|abs }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Filter("abs", vec![Tuple(vec![NumLit("1")])]),
+            Expr::Filter(Filter {
+                name: "abs",
+                arguments: vec![Expr::Tuple(vec![Expr::NumLit("1")])]
+            }),
         )],
     );
     assert_eq!(
         Ast::from_str("{{ (1,) | abs }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "|",
-                Box::new(Tuple(vec![NumLit("1")])),
-                Box::new(Var("abs"))
+                Box::new(Expr::Tuple(vec![Expr::NumLit("1")])),
+                Box::new(Expr::Var("abs"))
             ),
         )],
     );
@@ -680,17 +740,20 @@ fn test_parse_tuple() {
         Ast::from_str("{{ (1, 2)|abs }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Filter("abs", vec![Tuple(vec![NumLit("1"), NumLit("2")])]),
+            Expr::Filter(Filter {
+                name: "abs",
+                arguments: vec![Expr::Tuple(vec![Expr::NumLit("1"), Expr::NumLit("2")])]
+            }),
         )],
     );
     assert_eq!(
         Ast::from_str("{{ (1, 2) | abs }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            BinOp(
+            Expr::BinOp(
                 "|",
-                Box::new(Tuple(vec![NumLit("1"), NumLit("2")])),
-                Box::new(Var("abs"))
+                Box::new(Expr::Tuple(vec![Expr::NumLit("1"), Expr::NumLit("2")])),
+                Box::new(Expr::Var("abs"))
             ),
         )],
     );
@@ -766,14 +829,20 @@ fn test_parse_array() {
         Ast::from_str("{{ []|foo }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Expr::Filter("foo", vec![Expr::Array(vec![])])
+            Expr::Filter(Filter {
+                name: "foo",
+                arguments: vec![Expr::Array(vec![])]
+            })
         )],
     );
     assert_eq!(
         Ast::from_str("{{ []| foo }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Expr::Filter("foo", vec![Expr::Array(vec![])])
+            Expr::Filter(Filter {
+                name: "foo",
+                arguments: vec![Expr::Array(vec![])]
+            })
         )],
     );
     assert_eq!(
diff --git a/book/src/template_syntax.md b/book/src/template_syntax.md
index 6117f4306..e9d9aed0a 100644
--- a/book/src/template_syntax.md
+++ b/book/src/template_syntax.md
@@ -98,6 +98,17 @@ blocks**:
 
 The `lower` filter will be applied on the whole content.
 
+Just like filters, you can combine them:
+
+```text
+{% filter lower|capitalize %}
+    {{ t }} / HELLO / {{ u }}
+{% endfilter %}
+```
+
+In this case, `lower` will be called and then `capitalize` will be
+called on what `lower` returned.
+
 ## Whitespace control
 
 Askama considers all tabs, spaces, newlines and carriage returns to be
diff --git a/testing/tests/filter_block.rs b/testing/tests/filter_block.rs
index 029ce1374..55f7e1bad 100644
--- a/testing/tests/filter_block.rs
+++ b/testing/tests/filter_block.rs
@@ -128,3 +128,22 @@ fn filter_block_not_html_escape() {
     let template = E;
     assert_eq!(template.render().unwrap(), r#"<block>"#);
 }
+
+// This test checks that the filter chaining is working as expected.
+#[derive(Template)]
+#[template(
+    source = r#"{% filter lower|indent(2)|capitalize -%}
+HELLO
+{{v}}
+{%- endfilter %}"#,
+    ext = "txt"
+)]
+struct F {
+    v: &'static str,
+}
+
+#[test]
+fn filter_block_chaining() {
+    let template = F { v: "pIKA" };
+    assert_eq!(template.render().unwrap(), "Hello\n  pika");
+}