commit 141a5f1e310cfcf1ce910dd6991641b2383779c4 from: kirill date: Sun May 17 10:56:41 2026 UTC usr.sbin/httpd: add cache controls for static files Teach httpd to advertise static file revalidation by default with Cache-Control: no-cache, preserving the existing Last-Modified and If-Modified-Since flow; add a [no] static-cache-control directive for opting out, and advertise Vary: Accept-Encoding whenever gzip-static is enabled. OK: claudio@ commit - 3d9ef5b47476cc2d6aa05a8851e4acdfb02ad137 commit + 141a5f1e310cfcf1ce910dd6991641b2383779c4 blob - fc37148221bb216be2cb642ddc0ea36cf5e6c0a2 blob + 146ed2c198b4bd9c58e62a88076f81c4e68075af --- config.c +++ config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.71 2026/05/11 22:33:10 kirill Exp $ */ +/* $OpenBSD: config.c,v 1.72 2026/05/17 10:56:41 kirill Exp $ */ /* * Copyright (c) 2011 - 2015 Reyk Floeter @@ -633,6 +633,11 @@ config_getserver_config(struct httpd *env, struct serv sizeof(srv_conf->path)); } + f = SRVFLAG_STATIC_CACHE_CONTROL | + SRVFLAG_NO_STATIC_CACHE_CONTROL; + if ((srv_conf->flags & f) == 0) + srv_conf->flags |= parent->flags & f; + f = SRVFLAG_SERVER_HSTS; srv_conf->flags |= parent->flags & f; srv_conf->hsts_max_age = parent->hsts_max_age; blob - 3053709a3599d68ef034fef746e85f4cedc6f93a blob + be31341b5faa470fc9b5e3449dfd2f3b56c05202 --- httpd.conf.5 +++ httpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: httpd.conf.5,v 1.129 2026/01/18 16:38:02 schwarze Exp $ +.\" $OpenBSD: httpd.conf.5,v 1.130 2026/05/17 10:56:41 kirill 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: January 18 2026 $ +.Dd $Mdocdate: May 17 2026 $ .Dt HTTPD.CONF 5 .Os .Sh NAME @@ -254,6 +254,18 @@ If is omitted, enable the banner for the current .Ic server if it was disabled globally. +.It Oo Ic no Oc Ic static-cache-control +Send +.Dq Cache-Control: no-cache +with static file responses. +When +.Ic gzip-static +is enabled, also send +.Dq Vary: Accept-Encoding +with static file responses. +This is enabled by default; use +.Ic no static-cache-control +to disable these headers. .It Ic block drop Drop the connection without sending an error page. .It Ic block Op Ic return Ar code Op Ar uri blob - 8fc5d2adb05cadcd1ce5e350fb063fe44608ea15 blob + 2f4952c4870fb915ea3dc15c3bb05cccc24253b0 --- httpd.h +++ httpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: httpd.h,v 1.171 2026/05/11 22:33:10 kirill Exp $ */ +/* $OpenBSD: httpd.h,v 1.172 2026/05/17 10:56:41 kirill Exp $ */ /* * Copyright (c) 2006 - 2015 Reyk Floeter @@ -393,6 +393,8 @@ SPLAY_HEAD(client_tree, client); #define SRVFLAG_NO_GZIP_STATIC 0x0000000010000000ULL #define SRVFLAG_LOCATION_FOUND 0x0000000040000000ULL #define SRVFLAG_LOCATION_NOT_FOUND 0x0000000080000000ULL +#define SRVFLAG_STATIC_CACHE_CONTROL 0x0000000100000000ULL +#define SRVFLAG_NO_STATIC_CACHE_CONTROL 0x0000000200000000ULL #define SRVFLAG_BITS \ "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX" \ @@ -401,7 +403,8 @@ SPLAY_HEAD(client_tree, client); "\21AUTH\22NO_AUTH\23BLOCK\24NO_BLOCK\25LOCATION_MATCH" \ "\26SERVER_MATCH\27SERVER_HSTS\30DEFAULT_TYPE\31PATH_REWRITE" \ "\32NO_PATH_REWRITE\34NO_BANNER\33GZIP_STATIC" \ - "\35NO_GZIP_STATIC\37LOCATION_FOUND\40LOCATION_NOT_FOUND" + "\35NO_GZIP_STATIC\37LOCATION_FOUND\40LOCATION_NOT_FOUND" \ + "\41STATIC_CACHE_CONTROL\42NO_STATIC_CACHE_CONTROL" #define TCPFLAG_NODELAY 0x01 #define TCPFLAG_NNODELAY 0x02 blob - 060a6671db52a49724b68deeb3b23193a5b9f910 blob + 9233cc6b8ad8265cabb69b419ffeb9c46971bc63 --- parse.y +++ parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.133 2026/05/11 22:33:10 kirill Exp $ */ +/* $OpenBSD: parse.y,v 1.134 2026/05/17 10:56:41 kirill Exp $ */ /* * Copyright (c) 2020 Matthias Pressfreund @@ -142,7 +142,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 BANNER +%token ERRDOCS GZIPSTATIC BANNER STATIC_CACHE_CONTROL %token STRING %token NUMBER %type port @@ -275,7 +275,8 @@ server : SERVER optmatch STRING { SERVER_REQUESTTIMEOUT; s->srv_conf.maxrequests = SERVER_MAXREQUESTS; s->srv_conf.maxrequestbody = SERVER_MAXREQUESTBODY; - s->srv_conf.flags = SRVFLAG_LOG; + s->srv_conf.flags = SRVFLAG_LOG | + SRVFLAG_STATIC_CACHE_CONTROL; if ($2) s->srv_conf.flags |= SRVFLAG_SERVER_MATCH; s->srv_conf.logformat = LOG_FORMAT_COMMON; @@ -558,6 +559,7 @@ serveroptsl : LISTEN ON STRING opttls port { | root | directory | banner + | static_cache_control | logformat | fastcgi | authenticate @@ -1252,6 +1254,16 @@ gzip_static : NO GZIPSTATIC { } ; +static_cache_control : NO STATIC_CACHE_CONTROL { + srv_conf->flags &= ~SRVFLAG_STATIC_CACHE_CONTROL; + srv_conf->flags |= SRVFLAG_NO_STATIC_CACHE_CONTROL; + } + | STATIC_CACHE_CONTROL { + srv_conf->flags &= ~SRVFLAG_NO_STATIC_CACHE_CONTROL; + srv_conf->flags |= SRVFLAG_STATIC_CACHE_CONTROL; + } + ; + tcpip : TCP '{' optnl tcpflags_l '}' | TCP tcpflags ; @@ -1511,6 +1523,7 @@ lookup(char *s) { "sack", SACK }, { "server", SERVER }, { "socket", SOCKET }, + { "static-cache-control", STATIC_CACHE_CONTROL }, { "strip", STRIP }, { "style", STYLE }, { "subdomains", SUBDOMAINS }, blob - b01e0180a5491375c7edc88f2472603a43c7374a blob + fb3ec67ae0e4b22d64f519bceb51b301c5b1e38b --- server_http.c +++ server_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_http.c,v 1.161 2026/03/02 19:24:58 rsadowski Exp $ */ +/* $OpenBSD: server_http.c,v 1.162 2026/05/17 10:56:41 kirill Exp $ */ /* * Copyright (c) 2020 Matthias Pressfreund @@ -1608,6 +1608,16 @@ server_response_http(struct client *clt, unsigned int kv_set(cl, "%lld", (long long)size) == -1)) return (-1); + if (srv_conf->flags & SRVFLAG_STATIC_CACHE_CONTROL) { + if (kv_add(&resp->http_headers, "Cache-Control", + "no-cache") == NULL) + return (-1); + if (srv_conf->flags & SRVFLAG_GZIP_STATIC && + kv_add(&resp->http_headers, "Vary", + "Accept-Encoding") == NULL) + return (-1); + } + /* Set last modification time */ if (server_http_time(mtime, tmbuf, sizeof(tmbuf)) <= 0 || kv_add(&resp->http_headers, "Last-Modified", tmbuf) == NULL)