commit 3f1c295feef22d2d6550dc7686e2c0f94b67d306 from: rsadowski date: Fri Nov 28 16:10:00 2025 UTC Add "no banner" option to suppress Server header Introduces a global and per-server "[no] banner" directive that prevents httpd from sending the Server HTTP response header and removes server identification from error documents. The SERVER_SOFTWARE CGI environment variable remains set as required by RFC 3875. Diff by Lloyd (thanks), ok kirill@ commit - 4525f6dfc124f7f28ef2bca450dcc8500826d19f commit + 3f1c295feef22d2d6550dc7686e2c0f94b67d306 blob - cdb04f1eb0e6eb9a341ed47e3194baaca28fb0d0 blob + b45081129b7477a642f044e465c62d7861fb925e --- config.c +++ config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.66 2025/11/12 11:24:04 deraadt Exp $ */ +/* $OpenBSD: config.c,v 1.67 2025/11/28 16:10:00 rsadowski Exp $ */ /* * Copyright (c) 2011 - 2015 Reyk Floeter @@ -638,6 +638,8 @@ config_getserver_config(struct httpd *env, struct serv (void)strlcpy(srv_conf->errdocroot, parent->errdocroot, sizeof(srv_conf->errdocroot)); + srv_conf->flags |= parent->flags & SRVFLAG_NO_BANNER; + DPRINTF("%s: %s %d location \"%s\", " "parent \"%s[%u]\", flags: %s", __func__, ps->ps_title[privsep_process], ps->ps_instance, blob - 79411a7e9412ede10e4e3f21d9f613badff5f83e blob + b3673284e0b855fda9441b79a742105ba54610a0 --- httpd.conf.5 +++ httpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: httpd.conf.5,v 1.127 2025/07/08 14:26:45 schwarze Exp $ +.\" $OpenBSD: httpd.conf.5,v 1.128 2025/11/28 16:10:00 rsadowski Exp $ .\" .\" Copyright (c) 2014, 2015 Reyk Floeter .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 8 2025 $ +.Dd $Mdocdate: November 28 2025 $ .Dt HTTPD.CONF 5 .Os .Sh NAME @@ -171,6 +171,14 @@ If not specified, it defaults to within the .Xr chroot 2 directory. +.It Ic no banner +Do not send the +.Va Server +HTTP response header, and hide the server software name in error documents. +The +.Va SERVER_SOFTWARE +CGI environment variable is always set in accordance with +.%R RFC 3875 . .It Ic prefork Ar number Run the specified number of server processes. This increases the performance and prevents delays when connecting @@ -232,6 +240,20 @@ and must be readable by the www user. Use the .Ic no authenticate directive to disable authentication in a location. +.It Oo Ic no Oc Ic banner +When prefixed with the +.Ic no +keyword, +suppress the +.Va Server +HTTP response header, and hide the server software name in error documents for +the current +.Ic server. +If +.Ic no +is omitted, enable the banner for the current +.Ic server +if it was disabled globally. .It Ic block drop Drop the connection without sending an error page. .It Ic block Op Ic return Ar code Op Ar uri @@ -478,6 +500,7 @@ and for generic ones later on. A location section may include most of the server configuration rules except .Ic alias , +.Ic banner , .Ic connection , .Ic errdocs , .Ic hsts , blob - 40bcaf6cf35f88c031ff9d8566e66ac290253571 blob + baf11d1b52348e7ff1881be81e9f7ee1cd7cda3f --- httpd.h +++ httpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: httpd.h,v 1.166 2025/11/27 15:22:45 rsadowski Exp $ */ +/* $OpenBSD: httpd.h,v 1.167 2025/11/28 16:10:00 rsadowski Exp $ */ /* * Copyright (c) 2006 - 2015 Reyk Floeter @@ -389,6 +389,7 @@ SPLAY_HEAD(client_tree, client); #define SRVFLAG_PATH_REWRITE 0x01000000 #define SRVFLAG_NO_PATH_REWRITE 0x02000000 #define SRVFLAG_GZIP_STATIC 0x04000000 +#define SRVFLAG_NO_BANNER 0x08000000 #define SRVFLAG_LOCATION_FOUND 0x40000000 #define SRVFLAG_LOCATION_NOT_FOUND 0x80000000 @@ -398,7 +399,7 @@ SPLAY_HEAD(client_tree, client); "\14SYSLOG\15NO_SYSLOG\16TLS\17ACCESS_LOG\20ERROR_LOG" \ "\21AUTH\22NO_AUTH\23BLOCK\24NO_BLOCK\25LOCATION_MATCH" \ "\26SERVER_MATCH\27SERVER_HSTS\30DEFAULT_TYPE\31PATH_REWRITE" \ - "\32NO_PATH_REWRITE\33GZIP_STATIC\37LOCATION_FOUND" \ + "\32NO_PATH_REWRITE\34NO_BANNER\33GZIP_STATIC\37LOCATION_FOUND" \ "\40LOCATION_NOT_FOUND" #define TCPFLAG_NODELAY 0x01 blob - d565e61d727d98a0d09ed2a19fd08e608aff3298 blob + 326b249662f3eb72fc53110facb2f4df953932a7 --- parse.y +++ parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.129 2025/11/12 11:24:04 deraadt Exp $ */ +/* $OpenBSD: parse.y,v 1.130 2025/11/28 16:10:00 rsadowski Exp $ */ /* * Copyright (c) 2020 Matthias Pressfreund @@ -141,7 +141,7 @@ typedef struct { %token TIMEOUT TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD REQUEST %token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS REWRITE %token CA CLIENT CRL OPTIONAL PARAM FORWARDED FOUND NOT -%token ERRDOCS GZIPSTATIC +%token ERRDOCS GZIPSTATIC BANNER %token STRING %token NUMBER %type port @@ -227,6 +227,9 @@ main : PREFORK NUMBER { | LOGDIR STRING { conf->sc_logdir = $2; } + | NO BANNER { + conf->sc_flags |= SRVFLAG_NO_BANNER; + } | DEFAULT TYPE mediastring { memcpy(&conf->sc_default_type, &media, sizeof(struct media_type)); @@ -300,6 +303,9 @@ server : SERVER optmatch STRING { s->srv_conf.hsts_max_age = SERVER_HSTS_DEFAULT_AGE; + if (conf->sc_flags & SRVFLAG_NO_BANNER) + s->srv_conf.flags |= SRVFLAG_NO_BANNER; + (void)strlcpy(s->srv_conf.errdocroot, conf->sc_errdocroot, sizeof(s->srv_conf.errdocroot)); @@ -550,6 +556,7 @@ serveroptsl : LISTEN ON STRING opttls port { | request | root | directory + | banner | logformat | fastcgi | authenticate @@ -687,6 +694,22 @@ serveroptsl : LISTEN ON STRING opttls port { } ; +banner : BANNER { + if (parentsrv != NULL) { + yyerror("banner inside location"); + YYERROR; + } + srv->srv_conf.flags &= ~SRVFLAG_NO_BANNER; + } + | NO BANNER { + if (parentsrv != NULL) { + yyerror("no banner inside location"); + YYERROR; + } + srv->srv_conf.flags |= SRVFLAG_NO_BANNER; + } + ; + optfound : /* empty */ { $$ = 0; } | FOUND { $$ = 1; } | NOT FOUND { $$ = -1; } @@ -1428,6 +1451,7 @@ lookup(char *s) { "authenticate", AUTHENTICATE}, { "auto", AUTO }, { "backlog", BACKLOG }, + { "banner", BANNER }, { "block", BLOCK }, { "body", BODY }, { "buffer", BUFFER }, blob - 70bd78c082028e15efdf2a577dad13c64440ba0f blob + f3c01a459b08fdaba7be5db1ec4b9ec3cdf68862 --- server_fcgi.c +++ server_fcgi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_fcgi.c,v 1.97 2023/11/08 19:19:10 millert Exp $ */ +/* $OpenBSD: server_fcgi.c,v 1.98 2025/11/28 16:10:00 rsadowski Exp $ */ /* * Copyright (c) 2014 Florian Obser @@ -345,6 +345,7 @@ server_fcgi(struct httpd *env, struct client *clt) goto fail; } + /* RFC 3875 requires this variable always be present */ if (fcgi_add_param(¶m, "SERVER_SOFTWARE", HTTPD_SERVERNAME, clt) == -1) { errstr = "failed to encode param"; @@ -659,9 +660,12 @@ server_fcgi_header(struct client *clt, unsigned int co kv_set(&resp->http_pathquery, "%s", error) == -1) return (-1); - /* Add headers */ - if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL) - return (-1); + /* Add server banner header to response unless suppressed */ + if ((srv_conf->flags & SRVFLAG_NO_BANNER) == 0) { + if (kv_add(&resp->http_headers, "Server", + HTTPD_SERVERNAME) == NULL) + return (-1); + } if (clt->clt_fcgi.type == FCGI_END_REQUEST || EVBUFFER_LENGTH(clt->clt_srvevb) == 0) { blob - aef4e4567fc7544b0d99f5a54799b5a171bd7734 blob + f6412e80bf7e93b6807b4c66464c6782713e3747 --- server_http.c +++ server_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_http.c,v 1.155 2024/12/22 13:51:42 florian Exp $ */ +/* $OpenBSD: server_http.c,v 1.156 2025/11/28 16:10:00 rsadowski Exp $ */ /* * Copyright (c) 2020 Matthias Pressfreund @@ -890,6 +890,7 @@ server_abort_http(struct client *clt, unsigned int cod char *httpmsg, *body = NULL, *extraheader = NULL; char tmbuf[32], hbuf[128], *hstsheader = NULL; char *clenheader = NULL; + char *bannerheader = NULL, *bannertoken = NULL; char buf[IBUF_READ_SIZE]; char *escapedmsg = NULL; char cstr[5]; @@ -981,7 +982,11 @@ server_abort_http(struct client *clt, unsigned int cod body = replace_var(body, "$HTTP_ERROR", httperr); body = replace_var(body, "$RESPONSE_CODE", cstr); - body = replace_var(body, "$SERVER_SOFTWARE", HTTPD_SERVERNAME); + /* Check if server banner is suppressed */ + if ((srv_conf->flags & SRVFLAG_NO_BANNER) == 0) + body = replace_var(body, "$SERVER_SOFTWARE", HTTPD_SERVERNAME); + else + body = replace_var(body, "$SERVER_SOFTWARE", ""); bodylen = strlen(body); goto send; @@ -994,6 +999,14 @@ server_abort_http(struct client *clt, unsigned int cod "body { background-color: #1E1F21; color: #EEEFF1; }\n" "a { color: #BAD7FF; }\n}"; + /* If banner is suppressed, don't write it to the error document */ + if ((srv_conf->flags & SRVFLAG_NO_BANNER) == 0) + if (asprintf(&bannertoken, "
\n
%s
\n", + HTTPD_SERVERNAME) == -1) { + bannertoken = NULL; + goto done; + } + /* Generate simple HTML error document */ if ((bodylen = asprintf(&body, "\n" @@ -1005,10 +1018,11 @@ server_abort_http(struct client *clt, unsigned int cod "\n" "\n" "

%03d %s

\n" - "
\n
%s
\n" + "%s" "\n" "\n", - code, httperr, style, code, httperr, HTTPD_SERVERNAME)) == -1) { + code, httperr, style, code, httperr, + bannertoken == NULL ? "" : bannertoken)) == -1) { body = NULL; goto done; } @@ -1037,11 +1051,19 @@ server_abort_http(struct client *clt, unsigned int cod } } + /* If banner is suppressed, don't write a Server header */ + if ((srv_conf->flags & SRVFLAG_NO_BANNER) == 0) + if (asprintf(&bannerheader, "Server: %s\r\n", + HTTPD_SERVERNAME) == -1) { + bannerheader = NULL; + goto done; + } + /* Add basic HTTP headers */ if (asprintf(&httpmsg, "HTTP/1.0 %03d %s\r\n" "Date: %s\r\n" - "Server: %s\r\n" + "%s" "Connection: close\r\n" "Content-Type: text/html\r\n" "%s" @@ -1049,7 +1071,8 @@ server_abort_http(struct client *clt, unsigned int cod "%s" "\r\n" "%s", - code, httperr, tmbuf, HTTPD_SERVERNAME, + code, httperr, tmbuf, + bannerheader == NULL ? "" : bannerheader, clenheader == NULL ? "" : clenheader, extraheader == NULL ? "" : extraheader, hstsheader == NULL ? "" : hstsheader, @@ -1066,6 +1089,8 @@ server_abort_http(struct client *clt, unsigned int cod free(extraheader); free(hstsheader); free(clenheader); + free(bannerheader); + free(bannertoken); if (msg == NULL) msg = "\"\""; if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1) { @@ -1558,10 +1583,12 @@ server_response_http(struct client *clt, unsigned int kv_set(&resp->http_pathquery, "%s", error) == -1) return (-1); - /* Add headers */ - if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL) - return (-1); - + /* Add server banner header to response unless suppressed */ + if ((srv_conf->flags & SRVFLAG_NO_BANNER) == 0) { + if (kv_add(&resp->http_headers, "Server", + HTTPD_SERVERNAME) == NULL) + return (-1); + } /* Is it a persistent connection? */ if (clt->clt_persist) { if (kv_add(&resp->http_headers,