From: Mischa Subject: Re: httpd: add header block/drop rules for request filtering To: Rafael Sadowski Cc: tech@openbsd.org Date: Mon, 21 Sep 2026 18:49:56 +0200 Hi Rafael, Thank you for the reminder! Running it as we speak, and already like it a lot! Mischa On 2026-09-21 16:02, Rafael Sadowski wrote: > Hi tech@ > > during EuroBSDCon26, Purple Rain (secbsd.com) came up to me and showed > me his httpd diff. The idea was to block Ai scrapers by the user-agents > header because his site had crashed under the load. > > My answer was that you can also do this with relayd(8) in front of > http(8). However, I understand that not everyone wants to run relayd(8) > for a single httpd. > > His idea was to do this for user agents. I incorporated the whole thing > into our "header" syntax and made it generic. Now you can block (with > an > HTTP status code) or drop anything request you want based on key/value > header pairs. > > Here are a few use cases: > > 1.) Drop all the Ai scrapers > > $ cat ai_scrapers.conf > # BLOCK AI CRAWLERS AND TRAINING > > header drop "user-agent" "*bot*" > header block "user-agent" "addsearchbot*" 403 > header block "user-agent" "agenttimes*" 403 > header block "user-agent" "ai2bot*" 403 > header block "user-agent" "aihitbot*" 403 > header block "user-agent" "aiwebindex*" 403 > header block "user-agent" "amazon*" 403 > header block "user-agent" "amzn*" 403 > header block "user-agent" "andibot*" 403 > header block "user-agent" "anomura*" 403 > header drop "user-agent" "anthropic*" > header block "user-agent" "apify*" 403 > header block "user-agent" "applebot*" 403 > header block "user-agent" "aranet*" 403 > header block "user-agent" "atlassian-bot*" 403 > header block "user-agent" "awario*" 403 > header block "user-agent" "azureai*" 403 > ... > > server "default" { > listen on * port 80 > # block Ai and crawlers > include "/etc/ai_scrapers.conf" > location "/*" { > root "/htdocs/localhost" > } > } > > 2.) block with redirect > > header block "user-agent" "amazon*" 301 "https://amazon.com" > > 2.) block with message > > header block "user-agent" "amazon*" 404 "bye bye my love" > > I'm not sure if we want this in 8.0 or if we should wait until after > the > release. Of course, it would be useful to have it in the release. > > Purple Rain tested this diff in production. (Thanks) > > Feedback welcome. > > Rafael > > commit 1cb5c4a126af863bbb62b5594a1cf2253167cde6 > Author: Rafael Sadowski > Date: Tue Sep 15 18:43:00 2026 +0200 > > httpd: add header block/drop rules for request filtering > > With this incoming requests can also be rejected based on the value > of a > request header. Valid options are: > > block name value code [arg] > Close the connection with an error response when a > request header matches. Both name and value are shell- > style patterns and are matched case-insensitively against > the header name and value. code must be a valid HTTP > status code. For codes in the 3xx range, arg is required > and sent as the "Location" header. It must start with > "http://" or "https://". For all other codes, arg is > optional and used as the log message identifying the > rule. > > drop name value > Silently close the connection without sending a response > when a request header matches, using the same pattern > rules as block. > > Based on a diff from Purple Rain from SecBSD, who wrote a initial > version to block Ai- and other Scraper. Also requested and tested > by Mischa. > > diff --git a/config.c b/config.c > index ecaa59a..c7b933f 100644 > --- a/config.c > +++ b/config.c > @@ -198,6 +198,7 @@ clear_config_server_ptrs(struct server_config *cfg) > /* clear TAILQ_HEAD */ > memset(&cfg->fcgiparams, 0, sizeof(cfg->fcgiparams)); > memset(&cfg->headers, 0, sizeof(cfg->headers)); > + memset(&cfg->header_rules, 0, sizeof(cfg->header_rules)); > > /* clear TAILQ_ENTRY */ > memset(&cfg->entry, 0, sizeof(cfg->entry)); > @@ -295,6 +296,11 @@ config_setserver(struct httpd *env, struct server > *srv) > if (config_setserver_headers(env, srv) == -1) > return (-1); > > + /* Configure headers rules if necessary. */ > + config_inherit_header_rules(env, srv); > + if (config_setserver_header_rules(env, srv) == -1) > + return (-1); > + > /* Close server socket early to prevent fd exhaustion in the parent. > */ > if (srv->srv_s != -1) { > close(srv->srv_s); > @@ -521,6 +527,124 @@ config_inherit_headers(struct httpd *env, struct > server *srv) > TAILQ_CONCAT(&srv_conf->headers, &inherited, entry); > } > > +int > +config_getserver_header_rules(struct httpd *env, struct imsg *imsg) > +{ > + struct server_config *srv_conf; > + struct header_rule *rule; > + struct header_rule_imsg hmsg; > + struct ibuf ibuf; > + > + if (imsg_get_ibuf(imsg, &ibuf) == -1 || > + ibuf_get(&ibuf, &hmsg, sizeof(hmsg)) == -1) { > + log_debug("%s: invalid message", __func__); > + return (-1); > + } > + > + if ((srv_conf = serverconfig_byid(hmsg.id)) == NULL) { > + log_debug("%s: invalid config id", __func__); > + return (-1); > + } > + > + if ((rule = calloc(1, sizeof(*rule))) == NULL) > + fatal("header rule out of memory"); > + > + rule->name = ibuf_get_string(&ibuf, hmsg.namelen); > + rule->value = ibuf_get_string(&ibuf, hmsg.vallen); > + rule->return_uri = ibuf_get_string(&ibuf, hmsg.urilen); > + > + if (rule->name == NULL || rule->value == NULL || > + rule->return_uri == NULL) { > + free(rule->name); > + free(rule->value); > + free(rule->return_uri); > + free(rule); > + return (-1); > + } > + rule->action = hmsg.action; > + rule->return_code = hmsg.return_code; > + > + TAILQ_INSERT_TAIL(&srv_conf->header_rules, rule, entry); > + return (0); > +} > + > +/* > + * Inherit header rules from parent server > + */ > +void > +config_inherit_header_rules(struct httpd *env, struct server *srv) > +{ > + struct server *parent_srv; > + struct server_config *srv_conf = &srv->srv_conf; > + struct header_rule *rule, *nrule; > + struct server_header_rules inherited; > + > + if (!(srv_conf->flags & SRVFLAG_LOCATION)) > + return; > + > + /* Find parent server by parent_id */ > + TAILQ_FOREACH(parent_srv, env->sc_servers, srv_entry) { > + if (parent_srv->srv_conf.id == srv_conf->parent_id) > + break; > + } > + > + if (parent_srv == NULL) > + return; > + > + TAILQ_INIT(&inherited); > + > + TAILQ_FOREACH(rule, &parent_srv->srv_conf.header_rules, entry) { > + nrule = header_rule_dup(rule); > + TAILQ_INSERT_TAIL(&inherited, nrule, entry); > + DPRINTF("%s: inheriting header rule \"%s\" from parent \"%s\" " > + "to location \"%s\"", __func__, rule->name, > + parent_srv->srv_conf.name, srv_conf->location); > + } > + > + TAILQ_CONCAT(&srv_conf->header_rules, &inherited, entry); > +} > + > +int > +config_setserver_header_rules(struct httpd *env, struct server *srv) > +{ > + struct privsep *ps = env->sc_ps; > + struct server_config *srv_conf = &srv->srv_conf; > + struct header_rule *rule; > + struct header_rule_imsg hmsg; > + struct iovec iov[4]; > + > + DPRINTF("%s: sending header rules for \"%s[%u]\" to %s fd %d", > + __func__, srv_conf->name, srv_conf->id, > ps->ps_title[PROC_SERVER], > + srv->srv_s); > + > + TAILQ_FOREACH(rule, &srv_conf->header_rules, entry) { > + hmsg.id = srv_conf->id; > + > + hmsg.namelen = strlen(rule->name); > + hmsg.vallen = strlen(rule->value); > + hmsg.return_code = rule->return_code; > + hmsg.action = rule->action; > + hmsg.urilen = strlen(rule->return_uri); > + > + iov[0].iov_base = &hmsg; > + iov[0].iov_len = sizeof(hmsg); > + iov[1].iov_base = rule->name; > + iov[1].iov_len = hmsg.namelen; > + iov[2].iov_base = rule->value; > + iov[2].iov_len = hmsg.vallen; > + iov[3].iov_base = rule->return_uri; > + iov[3].iov_len = hmsg.urilen; > + > + if (proc_composev(ps, PROC_SERVER, IMSG_CFG_HEADER_RULES, > + iov, 4) != 0) { > + log_warn("%s: failed to compose IMSG_CFG_HEADER_RULES " > + "for `%s'", __func__, srv_conf->name); > + return (-1); > + } > + } > + return (0); > +} > + > int > config_setserver_headers(struct httpd *env, struct server *srv) > { > @@ -842,6 +966,7 @@ config_getserver(struct httpd *env, struct imsg > *imsg) > srv->srv_s = fd; > > TAILQ_INIT(&srv->srv_conf.headers); > + TAILQ_INIT(&srv->srv_conf.header_rules); > TAILQ_INIT(&srv->srv_conf.fcgiparams); > > if (config_getserver_auth(env, &srv->srv_conf) != 0) > diff --git a/httpd.c b/httpd.c > index 738fca2..20c2da8 100644 > --- a/httpd.c > +++ b/httpd.c > @@ -1284,3 +1284,22 @@ header_dup(const struct custom_header *src) > h->flags = src->flags; > return (h); > } > + > +struct header_rule * > +header_rule_dup(const struct header_rule *src) > +{ > + struct header_rule *r; > + > + if ((r = calloc(1, sizeof(*r))) == NULL) > + fatal("out of memory"); > + if ((r->name = strdup(src->name)) == NULL || > + (r->value = strdup(src->value)) == NULL) > + fatal("out of memory"); > + > + r->action = src->action; > + r->return_code = src->return_code; > + if ((r->return_uri = strdup(src->return_uri)) == NULL) > + fatal("out of memory"); > + > + return (r); > +} > diff --git a/httpd.conf.5 b/httpd.conf.5 > index c288692..b6339f0 100644 > --- a/httpd.conf.5 > +++ b/httpd.conf.5 > @@ -535,6 +535,39 @@ block are inherited from the > context and override defined headers with the same name. > If you do not wish to inherit these, you can remove them again with > .Ic remove . > +.Pp > +Incoming requests can also be rejected based on the value of a request > +header. > +Valid options are: > +.Bl -tag -width Ds > +.It Ic block Ar name Ar value Ar code Op Ar arg > +Close the connection with an error response when a request header > +matches. > +Both > +.Ar name > +and > +.Ar value > +are shell-style patterns and are matched case-insensitively against > +the header name and value. > +.Ar code > +must be a valid HTTP status code. > +For codes in the 3xx range, > +.Ar arg > +is required and sent as the > +.Qq Location > +header. > +It must start with > +.Qq http:// > +or > +.Qq https:// . > +For all other codes, > +.Ar arg > +is optional and used as the log message identifying the rule. > +.It Ic drop Ar name Ar value > +Silently close the connection without sending a response when a > request > +header matches, using the same pattern rules as > +.Ic block . > +.El > .It Ic hsts Oo Ar option Oc > Enable HTTP Strict Transport Security. > Valid options are: > diff --git a/httpd.h b/httpd.h > index 71377cf..821eca2 100644 > --- a/httpd.h > +++ b/httpd.h > @@ -190,6 +190,7 @@ enum imsg_type { > IMSG_CFG_AUTH, > IMSG_CFG_FCGI, > IMSG_CFG_HEADERS, > + IMSG_CFG_HEADER_RULES, > IMSG_CFG_DONE, > IMSG_LOG_ACCESS, > IMSG_LOG_ERROR, > @@ -413,6 +414,12 @@ enum log_format { > LOG_FORMAT_FORWARDED > }; > > +enum header_action { > + HEADER_ACTION_DROP, > + HEADER_ACTION_RETURN, > + HEADER_ACTION_RDR > +}; > + > #define HEADER_REMOVE 0x01 > #define HEADER_ADD 0x02 > #define HEADER_SET 0x04 > @@ -425,6 +432,15 @@ struct header_imsg { > uint16_t vallen; > }; > > +struct header_rule_imsg { > + uint32_t id; /* server conf id */ > + uint32_t namelen; > + uint32_t vallen; > + uint32_t action; > + uint32_t return_code; > + uint32_t urilen; > +}; > + > struct log_file { > char log_name[PATH_MAX]; > int log_fd; > @@ -478,6 +494,17 @@ struct custom_header { > }; > TAILQ_HEAD(server_headers, custom_header); > > +struct header_rule { > + char *name; > + char *value; > + enum header_action action; > + u_int32_t return_code; > + char *return_uri; > + > + TAILQ_ENTRY(header_rule) entry; > +}; > +TAILQ_HEAD(server_header_rules, header_rule); > + > struct server_config { > uint32_t id; > uint32_t parent_id; > @@ -549,6 +576,7 @@ struct server_config { > struct server_fcgiparams fcgiparams; > int fcgistrip; > int fcgiallowchunked; > + struct server_header_rules header_rules; > struct server_headers headers; > char errdocroot[HTTPD_ERRDOCROOT_MAX]; > > @@ -634,6 +662,7 @@ int server_privinit(struct server *); > void server_purge(struct server *); > void serverconfig_free(struct server_config *); > void server_headers_free(struct server_headers *); > +void server_header_rules_free(struct server_header_rules *); > void serverconfig_reset(struct server_config *); > int server_socket_af(struct sockaddr_storage *, in_port_t); > in_port_t server_socket_getport(struct sockaddr_storage *); > @@ -678,6 +707,8 @@ void server_abort_http(struct client *, unsigned > int, > const char *); > int server_custom_headers(struct server_config *, > struct kvtree *, unsigned int); > +struct header_rule *server_match_header_rule(struct server_config *, > + struct http_descriptor *); > enum httpmethod server_httpmethod_byname(const char *); > const char *server_httpmethod_byid(unsigned int); > const char *server_httperror_byid(unsigned int); > @@ -762,6 +793,7 @@ void print_custom_header(const char *, > const struct custom_header *); > int header_exists(struct server_config *, const char *); > struct custom_header *header_dup(const struct custom_header *); > +struct header_rule *header_rule_dup(const struct header_rule *); > > extern struct httpd *httpd_env; > > @@ -810,10 +842,13 @@ int config_setserver(struct httpd *, struct > server *); > int config_setserver_tls(struct httpd *, struct server *); > int config_setserver_fcgiparams(struct httpd *, struct server *); > int config_setserver_headers(struct httpd *, struct server *); > +int config_setserver_header_rules(struct httpd *, struct server *); > void config_inherit_headers(struct httpd *, struct server *); > +void config_inherit_header_rules(struct httpd *, struct server *); > int config_getserver(struct httpd *, struct imsg *); > int config_getserver_tls(struct httpd *, struct imsg *); > int config_getserver_fcgiparams(struct httpd *, struct imsg *); > +int config_getserver_header_rules(struct httpd *, struct imsg *); > int config_getserver_headers(struct httpd *, struct imsg *); > int config_setmedia(struct httpd *, struct media_type *); > int config_getmedia(struct httpd *, struct imsg *); > diff --git a/parse.y b/parse.y > index 204a2dc..e1c1e9c 100644 > --- a/parse.y > +++ b/parse.y > @@ -341,6 +341,7 @@ server : SERVER optmatch STRING { > TAILQ_INIT(&srv->srv_hosts); > TAILQ_INIT(&srv_conf->fcgiparams); > TAILQ_INIT(&srv_conf->headers); > + TAILQ_INIT(&srv_conf->header_rules); > > TAILQ_INSERT_TAIL(&srv->srv_hosts, srv_conf, entry); > } '{' optnl serveropts_l '}' { > @@ -664,6 +665,7 @@ serveroptsl : LISTEN ON STRING opttls port { > srv_conf = &srv->srv_conf; > SPLAY_INIT(&srv->srv_clients); > TAILQ_INIT(&srv_conf->headers); > + TAILQ_INIT(&srv_conf->header_rules); > TAILQ_INIT(&srv_conf->fcgiparams); > } '{' optnl serveropts_l '}' { > struct server *s = NULL; > @@ -832,6 +834,91 @@ header : HEADER REMOVE STRING optalways { > hdr->flags |= HEADER_ALWAYS; > TAILQ_INSERT_TAIL(&srv->srv_conf.headers, hdr, entry); > } > + | HEADER BLOCK STRING STRING NUMBER optstring { > + struct header_rule *hrule; > + > + if ((hrule= calloc(1, sizeof(*hrule))) == NULL) > + fatal("out of memory"); > + > + hrule->action = HEADER_ACTION_RETURN; > + > + hrule->name = $3; > + hrule->value = $4; > + > + if ($5 < 100 || $5 >= 600) { > + log_warn("header rule return code number is" > + "outside of a valid range"); > + } > + > + hrule->return_code = $5; > + > + if (hrule->return_code >= 300 && > + hrule->return_code <= 399) { > + hrule->action = HEADER_ACTION_RDR; > + } > + > + switch (hrule->action) { > + case HEADER_ACTION_DROP: > + /* Handeled in header drop sysntax */ > + break; > + case HEADER_ACTION_RDR: > + if ($6 == NULL) { > + yyerror("missing return URI"); > + free($6); > + free(hrule->name); > + free(hrule->value); > + free(hrule); > + YYERROR; > + } > + hrule->return_uri = $6; > + break; > + case HEADER_ACTION_RETURN: > + hrule->return_uri = ($6 != NULL) ? $6 : > + strdup("blocked"); > + if (hrule->return_uri == NULL) { > + yyerror("out of memory"); > + free(hrule->name); > + free(hrule->value); > + free(hrule); > + YYERROR; > + } > + break; > + default: > + break; > + } > + > + if (hrule->action == HEADER_ACTION_RDR && > + (strncmp(hrule->return_uri, "http://", 7) != 0 && > + strncmp(hrule->return_uri, "https://", 8) != 0)) { > + yyerror("Redirect URI not starts with " > + "http:// or https://"); > + free(hrule->name); > + free(hrule->value); > + free(hrule->return_uri); > + free(hrule); > + YYERROR; > + } > + > + TAILQ_INSERT_TAIL(&srv->srv_conf.header_rules, hrule, entry); > + } > + | HEADER DROP STRING STRING { > + struct header_rule *hrule; > + > + if ((hrule= calloc(1, sizeof(*hrule))) == NULL) > + fatal("out of memory"); > + > + hrule->action = HEADER_ACTION_DROP; > + if ((hrule->return_uri = strdup("dropped")) == NULL) { > + yyerror("out of memory"); > + free(hrule); > + YYERROR; > + } > + > + hrule->name = $3; > + hrule->value = $4; > + > + TAILQ_INSERT_TAIL(&srv->srv_conf.header_rules, hrule, entry); > + } > ; > > optfound : /* empty */ { $$ = 0; } > @@ -2480,6 +2567,7 @@ server_inherit(struct server *src, struct > server_config *alias, > { > struct server *dst, *s, *dstl; > struct custom_header *hdr, *nhdr; > + struct header_rule *rule, *nrule; > > if ((dst = calloc(1, sizeof(*dst))) == NULL) > fatal("out of memory"); > @@ -2493,6 +2581,12 @@ server_inherit(struct server *src, struct > server_config *alias, > TAILQ_INSERT_TAIL(&dst->srv_conf.headers, nhdr, entry); > } > > + TAILQ_INIT(&dst->srv_conf.header_rules); > + TAILQ_FOREACH(rule, &src->srv_conf.header_rules, entry) { > + nrule = header_rule_dup(rule); > + TAILQ_INSERT_TAIL(&dst->srv_conf.header_rules, nrule, entry); > + } > + > if ((dst->srv_conf.tls_cert_file = > strdup(src->srv_conf.tls_cert_file)) == NULL) > fatal("out of memory"); > @@ -2590,6 +2684,13 @@ server_inherit(struct server *src, struct > server_config *alias, > TAILQ_INSERT_TAIL(&dstl->srv_conf.headers, nhdr, entry); > } > > + /* Copy header rules from source location */ > + TAILQ_INIT(&dstl->srv_conf.header_rules); > + TAILQ_FOREACH(rule, &s->srv_conf.header_rules, entry) { > + nrule = header_rule_dup(rule); > + TAILQ_INSERT_TAIL(&dstl->srv_conf.header_rules, nrule, entry); > + } > + > strlcpy(dstl->srv_conf.name, alias->name, > sizeof(dstl->srv_conf.name)); > > diff --git a/server.c b/server.c > index bc96722..9aab25c 100644 > --- a/server.c > +++ b/server.c > @@ -472,6 +472,7 @@ server_purge(struct server *srv) > } > > server_headers_free(&srv->srv_conf.headers); > + server_header_rules_free(&srv->srv_conf.header_rules); > tls_config_free(srv->srv_tls_config); > tls_free(srv->srv_tls_ctx); > > @@ -490,6 +491,19 @@ server_headers_free(struct server_headers > *headers) > } > } > > +void > +server_header_rules_free(struct server_header_rules *rules) > +{ > + struct header_rule *rule, *trule; > + > + TAILQ_FOREACH_SAFE(rule, rules, entry, trule) { > + free(rule->name); > + free(rule->value); > + free(rule->return_uri); > + free(rule); > + } > +} > + > void > serverconfig_free(struct server_config *srv_conf) > { > @@ -513,6 +527,7 @@ serverconfig_free(struct server_config *srv_conf) > free(param); > } > server_headers_free(&srv_conf->headers); > + server_header_rules_free(&srv_conf->header_rules); > } > > void > @@ -532,6 +547,7 @@ serverconfig_reset(struct server_config *srv_conf) > srv_conf->tls_ocsp_staple_file = NULL; > TAILQ_INIT(&srv_conf->fcgiparams); > TAILQ_INIT(&srv_conf->headers); > + TAILQ_INIT(&srv_conf->header_rules); > } > > struct server * > @@ -1395,6 +1411,10 @@ server_dispatch_parent(int fd, struct > privsep_proc *p, struct imsg *imsg) > if (config_getserver_headers(httpd_env, imsg) != 0) > return (-1); > break; > + case IMSG_CFG_HEADER_RULES: > + if (config_getserver_header_rules(httpd_env, imsg) != 0) > + return (-1); > + break; > case IMSG_CFG_DONE: > if (config_getcfg(httpd_env, imsg) != 0) > return (-1); > diff --git a/server_http.c b/server_http.c > index 52cbc5d..8e47be2 100644 > --- a/server_http.c > +++ b/server_http.c > @@ -52,6 +52,8 @@ int server_http_authenticate(struct server_config > *, > struct client *); > static int http_version_num(char *); > static int http_is_success(unsigned int code); > +static int match_header_rule(struct header_rule *, const char *, > + const char *); > char *server_expand_http(struct client *, const char *, > char *, size_t); > char *replace_var(char *, const char *, const char *); > @@ -229,6 +231,37 @@ http_is_success(unsigned int code) > return (code >= 200 && code < 400); > } > > +static int > +match_header_rule(struct header_rule *rule, const char *key, const > char *value) > +{ > + return (fnmatch(rule->name, key, FNM_CASEFOLD) == 0 && > + fnmatch(rule->value, value, FNM_CASEFOLD) == 0); > +} > + > +struct header_rule * > +server_match_header_rule(struct server_config *srv_conf, > + struct http_descriptor *desc) > +{ > + struct header_rule *rule; > + struct kv *hdr = NULL; > + struct kv *kv = NULL; > + > + RB_FOREACH(hdr, kvtree, &desc->http_headers) { > + TAILQ_FOREACH(rule, &srv_conf->header_rules, entry) { > + if (match_header_rule(rule, hdr->kv_key, hdr->kv_value)) > + return (rule); > + } > + TAILQ_FOREACH(kv, &hdr->kv_children, kv_entry) { > + TAILQ_FOREACH(rule, &srv_conf->header_rules, entry) { > + if (match_header_rule(rule, kv->kv_key, > + kv->kv_value)) > + return (rule); > + } > + } > + } > + return (NULL); > +} > + > void > server_read_http(struct bufferevent *bev, void *arg) > { > @@ -1354,6 +1387,7 @@ server_response(struct httpd *httpd, struct > client *clt) > int portval = -1, ret; > char *hostval, *query; > const char *errstr = NULL; > + struct header_rule *mrule = NULL; > > /* Preserve original path */ > if (desc->http_path == NULL || > @@ -1474,6 +1508,23 @@ server_response(struct httpd *httpd, struct > client *clt) > server_abort_http(clt, 500, desc->http_path); > return (-1); > } > + if ((mrule = server_match_header_rule(srv_conf, desc)) != NULL) { > + switch (mrule->action) { > + case HEADER_ACTION_DROP: > + server_close(clt, mrule->return_uri); > + return (-1); > + case HEADER_ACTION_RDR: > + server_abort_http(clt, mrule->return_code, > + mrule->return_uri); > + return (-1); > + case HEADER_ACTION_RETURN: > + server_abort_http(clt, mrule->return_code, > + mrule->return_uri); > + return (-1); > + default: > + break; > + } > + } > > /* Optional rewrite */ > if (srv_conf->flags & SRVFLAG_PATH_REWRITE) {