diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index 8f31bd18f..5d7f7c52b 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -696,6 +696,10 @@ static nxt_conf_vldt_object_t  nxt_conf_vldt_match_members[] = {
         .type       = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
         .validator  = nxt_conf_vldt_match_patterns_sets,
         .u.string   = "cookies"
+    }, {
+        .name       = nxt_string("if"),
+        .type       = NXT_CONF_VLDT_STRING,
+        .validator  = nxt_conf_vldt_if,
     },
 
     NXT_CONF_VLDT_END
diff --git a/src/nxt_http.h b/src/nxt_http.h
index fe5e72a87..5369c8e16 100644
--- a/src/nxt_http.h
+++ b/src/nxt_http.h
@@ -441,6 +441,9 @@ void nxt_h1p_complete_buffers(nxt_task_t *task, nxt_h1proto_t *h1p,
     nxt_bool_t all);
 nxt_msec_t nxt_h1p_conn_request_timer_value(nxt_conn_t *c, uintptr_t data);
 
+int nxt_http_cond_value(nxt_task_t *task, nxt_http_request_t *r,
+    nxt_tstr_cond_t *cond);
+
 extern const nxt_conn_state_t  nxt_h1p_idle_close_state;
 
 #endif  /* _NXT_HTTP_H_INCLUDED_ */
diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c
index 54d1bd27e..a7e9ff69a 100644
--- a/src/nxt_http_request.c
+++ b/src/nxt_http_request.c
@@ -24,8 +24,6 @@ static void nxt_http_request_proto_info(nxt_task_t *task,
 static void nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj,
     void *data);
 static void nxt_http_request_done(nxt_task_t *task, void *obj, void *data);
-static nxt_int_t nxt_http_request_access_log(nxt_task_t *task,
-    nxt_http_request_t *r, nxt_router_conf_t *rtcf);
 
 static u_char *nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now,
     struct tm *tm, size_t size, const char *format);
@@ -861,12 +859,12 @@ nxt_http_request_error_handler(nxt_task_t *task, void *obj, void *data)
 void
 nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data)
 {
-    nxt_int_t                ret;
     nxt_http_proto_t         proto;
     nxt_router_conf_t        *rtcf;
     nxt_http_request_t       *r;
     nxt_http_protocol_t      protocol;
     nxt_socket_conf_joint_t  *conf;
+    nxt_router_access_log_t  *access_log;
 
     r = obj;
     proto.any = data;
@@ -878,8 +876,10 @@ nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data)
         r->logged = 1;
 
         if (rtcf->access_log != NULL) {
-            ret = nxt_http_request_access_log(task, r, rtcf);
-            if (ret == NXT_OK) {
+            access_log = rtcf->access_log;
+
+            if (nxt_http_cond_value(task, r, &rtcf->log_cond)) {
+                access_log->handler(task, r, access_log, rtcf->log_format);
                 return;
             }
         }
@@ -911,57 +911,6 @@ nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data)
 }
 
 
-static nxt_int_t
-nxt_http_request_access_log(nxt_task_t *task, nxt_http_request_t *r,
-    nxt_router_conf_t *rtcf)
-{
-    nxt_int_t                ret;
-    nxt_str_t                str;
-    nxt_bool_t               expr;
-    nxt_router_access_log_t  *access_log;
-
-    access_log = rtcf->access_log;
-
-    expr = 1;
-
-    if (rtcf->log_expr != NULL) {
-
-        if (nxt_tstr_is_const(rtcf->log_expr)) {
-            nxt_tstr_str(rtcf->log_expr, &str);
-
-        } else {
-            ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state,
-                                      &r->tstr_cache, r, r->mem_pool);
-            if (nxt_slow_path(ret != NXT_OK)) {
-                return NXT_DECLINED;
-            }
-
-            nxt_tstr_query(task, r->tstr_query, rtcf->log_expr, &str);
-
-            if (nxt_slow_path(nxt_tstr_query_failed(r->tstr_query))) {
-                return NXT_DECLINED;
-            }
-        }
-
-        if (str.length == 0
-            || nxt_str_eq(&str, "0", 1)
-            || nxt_str_eq(&str, "false", 5)
-            || nxt_str_eq(&str, "null", 4)
-            || nxt_str_eq(&str, "undefined", 9))
-        {
-            expr = 0;
-        }
-    }
-
-    if (rtcf->log_negate ^ expr) {
-        access_log->handler(task, r, access_log, rtcf->log_format);
-        return NXT_OK;
-    }
-
-    return NXT_DECLINED;
-}
-
-
 static u_char *
 nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now, struct tm *tm,
     size_t size, const char *format)
@@ -1365,3 +1314,48 @@ nxt_http_cookie_hash(nxt_mp_t *mp, nxt_str_t *name)
 {
     return nxt_http_field_hash(mp, name, 1, NXT_HTTP_URI_ENCODING_NONE);
 }
+
+
+int
+nxt_http_cond_value(nxt_task_t *task, nxt_http_request_t *r,
+    nxt_tstr_cond_t *cond)
+{
+    nxt_int_t          ret;
+    nxt_str_t          str;
+    nxt_bool_t         expr;
+    nxt_router_conf_t  *rtcf;
+
+    rtcf = r->conf->socket_conf->router_conf;
+
+    expr = 1;
+
+    if (cond->expr != NULL) {
+
+        if (nxt_tstr_is_const(cond->expr)) {
+            nxt_tstr_str(cond->expr, &str);
+
+        } else {
+            ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state,
+                                      &r->tstr_cache, r, r->mem_pool);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return -1;
+            }
+
+            ret = nxt_tstr_query(task, r->tstr_query, cond->expr, &str);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return -1;
+            }
+        }
+
+        if (str.length == 0
+            || nxt_str_eq(&str, "0", 1)
+            || nxt_str_eq(&str, "false", 5)
+            || nxt_str_eq(&str, "null", 4)
+            || nxt_str_eq(&str, "undefined", 9))
+        {
+            expr = 0;
+        }
+    }
+
+    return cond->negate ^ expr;
+}
diff --git a/src/nxt_http_return.c b/src/nxt_http_return.c
index b50e4ad0e..a3551683e 100644
--- a/src/nxt_http_return.c
+++ b/src/nxt_http_return.c
@@ -24,8 +24,8 @@ static nxt_http_action_t *nxt_http_return(nxt_task_t *task,
     nxt_http_request_t *r, nxt_http_action_t *action);
 static nxt_int_t nxt_http_return_encode(nxt_mp_t *mp, nxt_str_t *encoded,
     const nxt_str_t *location);
-static void nxt_http_return_send_ready(nxt_task_t *task, void *obj, void *data);
-static void nxt_http_return_send_error(nxt_task_t *task, void *obj, void *data);
+static void nxt_http_return_send(nxt_task_t *task, nxt_http_request_t *r,
+    nxt_http_return_ctx_t *ctx);
 
 
 static const nxt_http_request_state_t  nxt_http_return_send_state;
@@ -120,8 +120,6 @@ nxt_http_return(nxt_task_t *task, nxt_http_request_t *r,
             ctx->encoded = conf->encoded;
         }
 
-        nxt_http_return_send_ready(task, r, ctx);
-
     } else {
         rtcf = r->conf->socket_conf->router_conf;
 
@@ -131,13 +129,15 @@ nxt_http_return(nxt_task_t *task, nxt_http_request_t *r,
             goto fail;
         }
 
-        nxt_tstr_query(task, r->tstr_query, conf->location, &ctx->location);
-
-        nxt_tstr_query_resolve(task, r->tstr_query, ctx,
-                               nxt_http_return_send_ready,
-                               nxt_http_return_send_error);
+        ret = nxt_tstr_query(task, r->tstr_query, conf->location,
+                             &ctx->location);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            goto fail;
+        }
     }
 
+    nxt_http_return_send(task, r, ctx);
+
     return NULL;
 
 fail:
@@ -174,15 +174,11 @@ nxt_http_return_encode(nxt_mp_t *mp, nxt_str_t *encoded,
 
 
 static void
-nxt_http_return_send_ready(nxt_task_t *task, void *obj, void *data)
+nxt_http_return_send(nxt_task_t *task, nxt_http_request_t *r,
+    nxt_http_return_ctx_t *ctx)
 {
-    nxt_int_t               ret;
-    nxt_http_field_t        *field;
-    nxt_http_request_t      *r;
-    nxt_http_return_ctx_t   *ctx;
-
-    r = obj;
-    ctx = data;
+    nxt_int_t         ret;
+    nxt_http_field_t  *field;
 
     if (ctx != NULL) {
         if (ctx->location.length > 0) {
@@ -216,17 +212,6 @@ nxt_http_return_send_ready(nxt_task_t *task, void *obj, void *data)
 }
 
 
-static void
-nxt_http_return_send_error(nxt_task_t *task, void *obj, void *data)
-{
-    nxt_http_request_t  *r;
-
-    r = obj;
-
-    nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
-}
-
-
 static const nxt_http_request_state_t  nxt_http_return_send_state
     nxt_aligned(64) =
 {
diff --git a/src/nxt_http_rewrite.c b/src/nxt_http_rewrite.c
index 661200efe..5de15ed7c 100644
--- a/src/nxt_http_rewrite.c
+++ b/src/nxt_http_rewrite.c
@@ -52,9 +52,8 @@ nxt_http_rewrite(nxt_task_t *task, nxt_http_request_t *r)
             return NXT_ERROR;
         }
 
-        nxt_tstr_query(task, r->tstr_query, action->rewrite, &str);
-
-        if (nxt_slow_path(nxt_tstr_query_failed(r->tstr_query))) {
+        ret = nxt_tstr_query(task, r->tstr_query, action->rewrite, &str);
+        if (nxt_slow_path(ret != NXT_OK)) {
             return NXT_ERROR;
         }
     }
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index 852f57397..bd0646f3b 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -51,6 +51,7 @@ typedef struct {
     nxt_conf_value_t               *query;
     nxt_conf_value_t               *source;
     nxt_conf_value_t               *destination;
+    nxt_conf_value_t               *condition;
 } nxt_http_route_match_conf_t;
 
 
@@ -138,6 +139,7 @@ typedef union {
 
 typedef struct {
     uint32_t                       items;
+    nxt_tstr_cond_t                condition;
     nxt_http_action_t              action;
     nxt_http_route_test_t          test[];
 } nxt_http_route_match_t;
@@ -193,8 +195,8 @@ static nxt_int_t nxt_http_action_resolve(nxt_task_t *task,
     nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action);
 static nxt_http_action_t *nxt_http_pass_var(nxt_task_t *task,
     nxt_http_request_t *r, nxt_http_action_t *action);
-static void nxt_http_pass_query_ready(nxt_task_t *task, void *obj, void *data);
-static void nxt_http_pass_query_error(nxt_task_t *task, void *obj, void *data);
+static void nxt_http_pass_query(nxt_task_t *task, nxt_http_request_t *r,
+    nxt_http_action_t *action);
 static nxt_int_t nxt_http_pass_find(nxt_mp_t *mp, nxt_router_conf_t *rtcf,
     nxt_str_t *pass, nxt_http_action_t *action);
 static nxt_int_t nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name,
@@ -350,6 +352,12 @@ static nxt_conf_map_t  nxt_http_route_match_conf[] = {
         NXT_CONF_MAP_PTR,
         offsetof(nxt_http_route_match_conf_t, destination),
     },
+
+    {
+        nxt_string("if"),
+        NXT_CONF_MAP_PTR,
+        offsetof(nxt_http_route_match_conf_t, condition),
+    },
 };
 
 
@@ -397,7 +405,9 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
     uint32_t                     n;
     nxt_mp_t                     *mp;
     nxt_int_t                    ret;
-    nxt_conf_value_t             *match_conf, *action_conf;
+    nxt_str_t                    str;
+    nxt_conf_value_t             *match_conf, *action_conf, *condition;
+    nxt_router_conf_t            *rtcf;
     nxt_http_route_test_t        *test;
     nxt_http_route_rule_t        *rule;
     nxt_http_route_table_t       *table;
@@ -405,6 +415,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
     nxt_http_route_addr_rule_t   *addr_rule;
     nxt_http_route_match_conf_t  mtcf;
 
+    static const nxt_str_t  if_path = nxt_string("/if");
     static const nxt_str_t  match_path = nxt_string("/match");
     static const nxt_str_t  action_path = nxt_string("/action");
 
@@ -413,9 +424,21 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
     n = (match_conf != NULL) ? nxt_conf_object_members_count(match_conf) : 0;
     size = sizeof(nxt_http_route_match_t) + n * sizeof(nxt_http_route_test_t *);
 
-    mp = tmcf->router_conf->mem_pool;
+    rtcf = tmcf->router_conf;
+    mp = rtcf->mem_pool;
+
+    condition = NULL;
+
+    if (match_conf != NULL) {
+        condition = nxt_conf_get_path(match_conf, &if_path);
 
-    match = nxt_mp_alloc(mp, size);
+        if (condition != NULL) {
+            n--;
+            size -= sizeof(nxt_http_route_test_t *);
+        }
+    }
+
+    match = nxt_mp_zalloc(mp, size);
     if (nxt_slow_path(match == NULL)) {
         return NULL;
     }
@@ -432,7 +455,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
         return NULL;
     }
 
-    if (n == 0) {
+    if (n == 0 && condition == NULL) {
         return match;
     }
 
@@ -445,6 +468,15 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
         return NULL;
     }
 
+    if (condition != NULL) {
+        nxt_conf_get_string(condition, &str);
+
+        ret = nxt_tstr_cond_compile(rtcf->tstr_state, &str, &match->condition);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NULL;
+        }
+    }
+
     test = &match->test[0];
 
     if (mtcf.scheme != NULL) {
@@ -1344,10 +1376,13 @@ nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r,
 
     action->u.pass = nxt_pointer_to(action, sizeof(nxt_http_action_t));
 
-    nxt_tstr_query(task, r->tstr_query, tstr, action->u.pass);
-    nxt_tstr_query_resolve(task, r->tstr_query, action,
-                           nxt_http_pass_query_ready,
-                           nxt_http_pass_query_error);
+    ret = nxt_tstr_query(task, r->tstr_query, tstr, action->u.pass);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        goto fail;
+    }
+
+    nxt_http_pass_query(task, r, action);
+
     return NULL;
 
 fail:
@@ -1358,16 +1393,13 @@ nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r,
 
 
 static void
-nxt_http_pass_query_ready(nxt_task_t *task, void *obj, void *data)
+nxt_http_pass_query(nxt_task_t *task, nxt_http_request_t *r,
+    nxt_http_action_t *action)
 {
-    nxt_int_t           ret;
-    nxt_router_conf_t   *rtcf;
-    nxt_http_action_t   *action;
-    nxt_http_status_t   status;
-    nxt_http_request_t  *r;
-
-    r = obj;
-    action = data;
+    nxt_int_t          ret;
+    nxt_router_conf_t  *rtcf;
+    nxt_http_status_t  status;
+
     rtcf = r->conf->socket_conf->router_conf;
 
     nxt_debug(task, "http pass lookup: %V", action->u.pass);
@@ -1386,17 +1418,6 @@ nxt_http_pass_query_ready(nxt_task_t *task, void *obj, void *data)
 }
 
 
-static void
-nxt_http_pass_query_error(nxt_task_t *task, void *obj, void *data)
-{
-    nxt_http_request_t  *r;
-
-    r = obj;
-
-    nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
-}
-
-
 static nxt_int_t
 nxt_http_pass_find(nxt_mp_t *mp, nxt_router_conf_t *rtcf, nxt_str_t *pass,
     nxt_http_action_t *action)
@@ -1607,6 +1628,12 @@ nxt_http_route_match(nxt_task_t *task, nxt_http_request_t *r,
     nxt_int_t              ret;
     nxt_http_route_test_t  *test, *end;
 
+    ret = nxt_http_cond_value(task, r, &match->condition);
+    if (ret <= 0) {
+        /* 0 => NULL, -1 => NXT_HTTP_ACTION_ERROR. */
+        return (nxt_http_action_t *) (intptr_t) ret;
+    }
+
     test = &match->test[0];
     end = test + match->items;
 
diff --git a/src/nxt_http_set_headers.c b/src/nxt_http_set_headers.c
index 25dd7478f..7fd6aba52 100644
--- a/src/nxt_http_set_headers.c
+++ b/src/nxt_http_set_headers.c
@@ -139,9 +139,8 @@ nxt_http_set_headers(nxt_http_request_t *r)
                 return NXT_ERROR;
             }
 
-            nxt_tstr_query(&r->task, r->tstr_query, hv->value, &value[i]);
-
-            if (nxt_slow_path(nxt_tstr_query_failed(r->tstr_query))) {
+            ret = nxt_tstr_query(&r->task, r->tstr_query, hv->value, &value[i]);
+            if (nxt_slow_path(ret != NXT_OK)) {
                 return NXT_ERROR;
             }
         }
diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c
index ee25015e2..67591595a 100644
--- a/src/nxt_http_static.c
+++ b/src/nxt_http_static.c
@@ -47,8 +47,8 @@ static nxt_http_action_t *nxt_http_static(nxt_task_t *task,
     nxt_http_request_t *r, nxt_http_action_t *action);
 static void nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r,
     nxt_http_static_ctx_t *ctx);
-static void nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data);
-static void nxt_http_static_send_error(nxt_task_t *task, void *obj, void *data);
+static void nxt_http_static_send(nxt_task_t *task, nxt_http_request_t *r,
+    nxt_http_static_ctx_t *ctx);
 static void nxt_http_static_next(nxt_task_t *task, nxt_http_request_t *r,
     nxt_http_static_ctx_t *ctx, nxt_http_status_t status);
 #if (NXT_HAVE_OPENAT2)
@@ -271,35 +271,44 @@ nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r,
         }
 #endif
 
-        nxt_http_static_send_ready(task, r, ctx);
-
     } else {
         rtcf = r->conf->socket_conf->router_conf;
 
         ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state,
                                   &r->tstr_cache, r, r->mem_pool);
         if (nxt_slow_path(ret != NXT_OK)) {
-            nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
-            return;
+            goto fail;
         }
 
-        nxt_tstr_query(task, r->tstr_query, share->tstr, &ctx->share);
+        ret = nxt_tstr_query(task, r->tstr_query, share->tstr, &ctx->share);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            goto fail;
+        }
 
 #if (NXT_HAVE_OPENAT2)
         if (conf->chroot != NULL && ctx->share_idx == 0) {
-            nxt_tstr_query(task, r->tstr_query, conf->chroot, &ctx->chroot);
+            ret = nxt_tstr_query(task, r->tstr_query, conf->chroot,
+                                 &ctx->chroot);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                goto fail;
+            }
         }
 #endif
+    }
+
+    nxt_http_static_send(task, r, ctx);
+
+    return;
+
+fail:
 
-        nxt_tstr_query_resolve(task, r->tstr_query, ctx,
-                               nxt_http_static_send_ready,
-                               nxt_http_static_send_error);
-     }
+    nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
 }
 
 
 static void
-nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data)
+nxt_http_static_send(nxt_task_t *task, nxt_http_request_t *r,
+    nxt_http_static_ctx_t *ctx)
 {
     size_t                  length, encode;
     u_char                  *p, *fname;
@@ -314,13 +323,9 @@ nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data)
     nxt_http_status_t       status;
     nxt_router_conf_t       *rtcf;
     nxt_http_action_t       *action;
-    nxt_http_request_t      *r;
     nxt_work_handler_t      body_handler;
-    nxt_http_static_ctx_t   *ctx;
     nxt_http_static_conf_t  *conf;
 
-    r = obj;
-    ctx = data;
     action = ctx->action;
     conf = action->u.conf;
     rtcf = r->conf->socket_conf->router_conf;
@@ -662,17 +667,6 @@ nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data)
 }
 
 
-static void
-nxt_http_static_send_error(nxt_task_t *task, void *obj, void *data)
-{
-    nxt_http_request_t  *r;
-
-    r = obj;
-
-    nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
-}
-
-
 static void
 nxt_http_static_next(nxt_task_t *task, nxt_http_request_t *r,
     nxt_http_static_ctx_t *ctx, nxt_http_status_t status)
diff --git a/src/nxt_router.h b/src/nxt_router.h
index cfc7258c5..06c6bb32a 100644
--- a/src/nxt_router.h
+++ b/src/nxt_router.h
@@ -54,8 +54,7 @@ typedef struct {
 
     nxt_router_access_log_t  *access_log;
     nxt_tstr_t               *log_format;
-    nxt_tstr_t               *log_expr;
-    uint8_t                  log_negate;  /* 1 bit */
+    nxt_tstr_cond_t          log_cond;
 } nxt_router_conf_t;
 
 
diff --git a/src/nxt_router_access_log.c b/src/nxt_router_access_log.c
index ff17b0b68..afecd0b13 100644
--- a/src/nxt_router_access_log.c
+++ b/src/nxt_router_access_log.c
@@ -26,10 +26,8 @@ typedef struct {
 static void nxt_router_access_log_writer(nxt_task_t *task,
     nxt_http_request_t *r, nxt_router_access_log_t *access_log,
     nxt_tstr_t *format);
-static void nxt_router_access_log_write_ready(nxt_task_t *task, void *obj,
-    void *data);
-static void nxt_router_access_log_write_error(nxt_task_t *task, void *obj,
-    void *data);
+static void nxt_router_access_log_write(nxt_task_t *task, nxt_http_request_t *r,
+    nxt_router_access_log_ctx_t *ctx);
 static void nxt_router_access_log_ready(nxt_task_t *task,
     nxt_port_recv_msg_t *msg, void *data);
 static void nxt_router_access_log_error(nxt_task_t *task,
@@ -145,15 +143,8 @@ nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf,
     if (alcf.expr != NULL) {
         nxt_conf_get_string(alcf.expr, &str);
 
-        if (str.length > 0 && str.start[0] == '!') {
-            rtcf->log_negate = 1;
-
-            str.start++;
-            str.length--;
-        }
-
-        rtcf->log_expr = nxt_tstr_compile(rtcf->tstr_state, &str, 0);
-        if (nxt_slow_path(rtcf->log_expr == NULL)) {
+        ret = nxt_tstr_cond_compile(rtcf->tstr_state, &str, &rtcf->log_cond);
+        if (nxt_slow_path(ret != NXT_OK)) {
             return NXT_ERROR;
         }
     }
@@ -180,8 +171,6 @@ nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r,
     if (nxt_tstr_is_const(format)) {
         nxt_tstr_str(format, &ctx->text);
 
-        nxt_router_access_log_write_ready(task, r, ctx);
-
     } else {
         rtcf = r->conf->socket_conf->router_conf;
 
@@ -191,36 +180,26 @@ nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r,
             return;
         }
 
-        nxt_tstr_query(task, r->tstr_query, format, &ctx->text);
-        nxt_tstr_query_resolve(task, r->tstr_query, ctx,
-                               nxt_router_access_log_write_ready,
-                               nxt_router_access_log_write_error);
-     }
+        ret = nxt_tstr_query(task, r->tstr_query, format, &ctx->text);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return;
+        }
+    }
+
+    nxt_router_access_log_write(task, r, ctx);
 }
 
 
 static void
-nxt_router_access_log_write_ready(nxt_task_t *task, void *obj, void *data)
+nxt_router_access_log_write(nxt_task_t *task, nxt_http_request_t *r,
+    nxt_router_access_log_ctx_t *ctx)
 {
-    nxt_http_request_t           *r;
-    nxt_router_access_log_ctx_t  *ctx;
-
-    r = obj;
-    ctx = data;
-
     nxt_fd_write(ctx->access_log->fd, ctx->text.start, ctx->text.length);
 
     nxt_http_request_close_handler(task, r, r->proto.any);
 }
 
 
-static void
-nxt_router_access_log_write_error(nxt_task_t *task, void *obj, void *data)
-{
-
-}
-
-
 void
 nxt_router_access_log_open(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
 {
diff --git a/src/nxt_tstr.c b/src/nxt_tstr.c
index 6f39cff28..50df4c475 100644
--- a/src/nxt_tstr.c
+++ b/src/nxt_tstr.c
@@ -36,14 +36,8 @@ struct nxt_tstr_query_s {
     nxt_tstr_state_t    *state;
     nxt_tstr_cache_t    *cache;
 
-    nxt_uint_t          waiting;
-    nxt_uint_t          failed;   /* 1 bit */
-
     void                *ctx;
     void                *data;
-
-    nxt_work_handler_t  ready;
-    nxt_work_handler_t  error;
 };
 
 
@@ -202,6 +196,26 @@ nxt_tstr_state_release(nxt_tstr_state_t *state)
 }
 
 
+nxt_int_t
+nxt_tstr_cond_compile(nxt_tstr_state_t *state, nxt_str_t *str,
+    nxt_tstr_cond_t *cond)
+{
+    if (str->length > 0 && str->start[0] == '!') {
+        cond->negate = 1;
+
+        str->start++;
+        str->length--;
+    }
+
+    cond->expr = nxt_tstr_compile(state, str, 0);
+    if (nxt_slow_path(cond->expr == NULL)) {
+        return NXT_ERROR;
+    }
+
+    return NXT_OK;
+}
+
+
 nxt_bool_t
 nxt_tstr_is_const(nxt_tstr_t *tstr)
 {
@@ -246,7 +260,7 @@ nxt_tstr_query_init(nxt_tstr_query_t **query_p, nxt_tstr_state_t *state,
 }
 
 
-void
+nxt_int_t
 nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr,
     nxt_str_t *val)
 {
@@ -254,11 +268,7 @@ nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr,
 
     if (nxt_tstr_is_const(tstr)) {
         nxt_tstr_str(tstr, val);
-        return;
-    }
-
-    if (nxt_slow_path(query->failed)) {
-        return;
+        return NXT_OK;
     }
 
     if (tstr->type == NXT_TSTR_VAR) {
@@ -267,8 +277,7 @@ nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr,
                                   tstr->flags & NXT_TSTR_LOGGING);
 
         if (nxt_slow_path(ret != NXT_OK)) {
-            query->failed = 1;
-            return;
+            return NXT_ERROR;
         }
 
     } else {
@@ -277,8 +286,7 @@ nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr,
                           tstr->u.js, val, query->ctx);
 
         if (nxt_slow_path(ret != NXT_OK)) {
-            query->failed = 1;
-            return;
+            return NXT_ERROR;
         }
 #endif
     }
@@ -294,43 +302,8 @@ nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr,
 
     nxt_debug(task, "tstr query: \"%V\", result: \"%V\"", &str, val);
 #endif
-}
-
-
-nxt_bool_t
-nxt_tstr_query_failed(nxt_tstr_query_t *query)
-{
-    return query->failed;
-}
-
 
-void
-nxt_tstr_query_resolve(nxt_task_t *task, nxt_tstr_query_t *query, void *data,
-    nxt_work_handler_t ready, nxt_work_handler_t error)
-{
-    query->data = data;
-    query->ready = ready;
-    query->error = error;
-
-    if (query->waiting == 0) {
-        nxt_work_queue_add(&task->thread->engine->fast_work_queue,
-                           query->failed ? query->error : query->ready,
-                           task, query->ctx, query->data);
-    }
-}
-
-
-void
-nxt_tstr_query_handle(nxt_task_t *task, nxt_tstr_query_t *query,
-    nxt_bool_t failed)
-{
-    query->failed |= failed;
-
-    if (--query->waiting == 0) {
-        nxt_work_queue_add(&task->thread->engine->fast_work_queue,
-                           query->failed ? query->error : query->ready,
-                           task, query->ctx, query->data);
-    }
+    return NXT_OK;
 }
 
 
diff --git a/src/nxt_tstr.h b/src/nxt_tstr.h
index a156732dd..aca74e20c 100644
--- a/src/nxt_tstr.h
+++ b/src/nxt_tstr.h
@@ -37,12 +37,20 @@ typedef enum {
 } nxt_tstr_flags_t;
 
 
+typedef struct {
+    nxt_tstr_t          *expr;
+    uint8_t             negate;  /* 1 bit */
+} nxt_tstr_cond_t;
+
+
 nxt_tstr_state_t *nxt_tstr_state_new(nxt_mp_t *mp, nxt_bool_t test);
 nxt_tstr_t *nxt_tstr_compile(nxt_tstr_state_t *state, const nxt_str_t *str,
     nxt_tstr_flags_t flags);
 nxt_int_t nxt_tstr_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error);
 nxt_int_t nxt_tstr_state_done(nxt_tstr_state_t *state, u_char *error);
 void nxt_tstr_state_release(nxt_tstr_state_t *state);
+nxt_int_t nxt_tstr_cond_compile(nxt_tstr_state_t *state, nxt_str_t *str,
+    nxt_tstr_cond_t *cond);
 
 nxt_bool_t nxt_tstr_is_const(nxt_tstr_t *tstr);
 void nxt_tstr_str(nxt_tstr_t *tstr, nxt_str_t *str);
@@ -50,13 +58,8 @@ void nxt_tstr_str(nxt_tstr_t *tstr, nxt_str_t *str);
 nxt_int_t nxt_tstr_query_init(nxt_tstr_query_t **query_p,
     nxt_tstr_state_t *state, nxt_tstr_cache_t *cache, void *ctx,
     nxt_mp_t *mp);
-void nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr,
-    nxt_str_t *val);
-nxt_bool_t nxt_tstr_query_failed(nxt_tstr_query_t *query);
-void nxt_tstr_query_resolve(nxt_task_t *task, nxt_tstr_query_t *query,
-    void *data, nxt_work_handler_t ready, nxt_work_handler_t error);
-void nxt_tstr_query_handle(nxt_task_t *task, nxt_tstr_query_t *query,
-    nxt_bool_t failed);
+nxt_int_t nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query,
+    nxt_tstr_t *tstr, nxt_str_t *val);
 void nxt_tstr_query_release(nxt_tstr_query_t *query);
 
 
diff --git a/src/nxt_var.c b/src/nxt_var.c
index 94d10cd85..16aa1c7ed 100644
--- a/src/nxt_var.c
+++ b/src/nxt_var.c
@@ -30,14 +30,8 @@ struct nxt_var_query_s {
 
     nxt_var_cache_t     cache;
 
-    nxt_uint_t          waiting;
-    nxt_uint_t          failed;   /* 1 bit */
-
     void                *ctx;
     void                *data;
-
-    nxt_work_handler_t  ready;
-    nxt_work_handler_t  error;
 };
 
 
diff --git a/test/test_routing.py b/test/test_routing.py
index 0b6eced27..c419779a0 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -2009,3 +2009,60 @@ def test_routes_match_destination_proxy():
     ), 'proxy configure'
 
     assert client.get()['status'] == 200, 'proxy'
+
+
+def test_routes_match_if():
+
+    def set_if(condition):
+        assert 'success' in client.conf(f'"{condition}"', 'routes/0/match/if')
+
+    def try_if(condition, status):
+        set_if(condition)
+        assert client.get(url=f'/{condition}')['status'] == status
+
+    assert 'success' in client.conf(
+        {
+            "listeners": {"*:8080": {"pass": "routes"}},
+            "routes": [
+                {
+                    "match": {"method": "GET"},
+                    "action": {"return": 200},
+                }
+            ],
+            "applications": {},
+        }
+    ), 'routing configure'
+
+    # const
+
+    try_if('', 404)
+    try_if('0', 404)
+    try_if('false', 404)
+    try_if('undefined', 404)
+    try_if('!', 200)
+    try_if('!null', 200)
+    try_if('1', 200)
+
+    # variable
+
+    set_if('$arg_foo')
+    assert client.get(url='/bar?bar')['status'] == 404
+    assert client.get(url='/foo_empty?foo')['status'] == 404
+    assert client.get(url='/foo?foo=1')['status'] == 200
+
+    set_if('!$arg_foo')
+    assert client.get(url='/bar?bar')['status'] == 200
+    assert client.get(url='/foo_empty?foo')['status'] == 200
+    assert client.get(url='/foo?foo=1')['status'] == 404
+
+    # njs
+
+    set_if('`${args.foo == \'1\'}`')
+    assert client.get(url='/foo_1?foo=1')['status'] == 200
+    assert client.get(url='/foo_2?foo=2')['status'] == 404
+
+    set_if('!`${args.foo == \'1\'}`')
+    assert client.get(url='/foo_1?foo=1')['status'] == 404
+    assert client.get(url='/foo_2?foo=2')['status'] == 200
+
+    assert 'error' in client.conf('$arg_', 'routes/0/match/if')