Download raw body.
httpd: add header block/drop rules for request filtering
On Mon, 21 Sep 2026 16:02:08 +0200,
Rafael Sadowski <rafael@sizeofvoid.org> 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.
>
It reads interesting not sure how it can be used against AI crawlers,
because shity one never respects User Agent, and sane one moves to proove
who they are by using Web Bot Auth.
Anyway, I don't object from this feature, and actually it can be useful for someone.
> 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) {
>
--
wbr, Kirill
httpd: add header block/drop rules for request filtering