Download raw body.
httpd: add header block/drop rules for request filtering
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 <rafael@sizeofvoid.org>
> 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) {
httpd: add header block/drop rules for request filtering