Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
httpd: add cache controls for static files
To:
OpenBSD tech <tech@openbsd.org>
Date:
Tue, 05 May 2026 21:58:01 +0200

Download raw body.

Thread
  • Kirill A. Korinsky:

    httpd: add cache controls for static files

tech@,

I'd like to 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] cache directive for opting out, and
advertise Vary: Accept-Encoding whenever gzip-static is enabled.

Ok?

Index: config.c
===================================================================
RCS file: /home/cvs/src/usr.sbin/httpd/config.c,v
diff -u -p -r1.69 config.c
--- config.c	2 Mar 2026 19:24:58 -0000	1.69
+++ config.c	5 May 2026 19:35:45 -0000
@@ -629,6 +629,10 @@ config_getserver_config(struct httpd *en
 			    sizeof(srv_conf->path));
 		}
 
+		f = SRVFLAG_CACHE|SRVFLAG_NO_CACHE;
+		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;
Index: httpd.conf.5
===================================================================
RCS file: /home/cvs/src/usr.sbin/httpd/httpd.conf.5,v
diff -u -p -r1.129 httpd.conf.5
--- httpd.conf.5	18 Jan 2026 16:38:02 -0000	1.129
+++ httpd.conf.5	5 May 2026 19:48:51 -0000
@@ -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 cache
+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 cache
+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
Index: httpd.h
===================================================================
RCS file: /home/cvs/src/usr.sbin/httpd/httpd.h,v
diff -u -p -r1.169 httpd.h
--- httpd.h	2 Mar 2026 19:24:58 -0000	1.169
+++ httpd.h	5 May 2026 19:41:51 -0000
@@ -390,6 +390,8 @@ SPLAY_HEAD(client_tree, client);
 #define SRVFLAG_NO_PATH_REWRITE	0x02000000
 #define SRVFLAG_GZIP_STATIC	0x04000000
 #define SRVFLAG_NO_BANNER	0x08000000
+#define SRVFLAG_CACHE		0x10000000
+#define SRVFLAG_NO_CACHE	0x20000000
 #define SRVFLAG_LOCATION_FOUND	0x40000000
 #define SRVFLAG_LOCATION_NOT_FOUND 0x80000000
 
@@ -399,8 +401,8 @@ 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\34NO_BANNER\33GZIP_STATIC\37LOCATION_FOUND"	\
-	"\40LOCATION_NOT_FOUND"
+	"\32NO_PATH_REWRITE\33GZIP_STATIC\34NO_BANNER\35CACHE"		\
+	"\36NO_CACHE\37LOCATION_FOUND\40LOCATION_NOT_FOUND"
 
 #define TCPFLAG_NODELAY		0x01
 #define TCPFLAG_NNODELAY	0x02
Index: parse.y
===================================================================
RCS file: /home/cvs/src/usr.sbin/httpd/parse.y,v
diff -u -p -r1.131 parse.y
--- parse.y	2 Mar 2026 19:24:58 -0000	1.131
+++ parse.y	5 May 2026 19:38:15 -0000
@@ -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 CACHE
 %token	<v.string>	STRING
 %token  <v.number>	NUMBER
 %type	<v.port>	port
@@ -275,7 +275,7 @@ 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_CACHE;
 			if ($2)
 				s->srv_conf.flags |= SRVFLAG_SERVER_MATCH;
 			s->srv_conf.logformat = LOG_FORMAT_COMMON;
@@ -558,6 +558,7 @@ serveroptsl	: LISTEN ON STRING opttls po
 		| root
 		| directory
 		| banner
+		| cache
 		| logformat
 		| fastcgi
 		| authenticate
@@ -1250,6 +1251,16 @@ gzip_static	: NO GZIPSTATIC		{
 		}
 		;
 
+cache		: NO CACHE		{
+			srv_conf->flags &= ~SRVFLAG_CACHE;
+			srv_conf->flags |= SRVFLAG_NO_CACHE;
+		}
+		| CACHE			{
+			srv_conf->flags &= ~SRVFLAG_NO_CACHE;
+			srv_conf->flags |= SRVFLAG_CACHE;
+		}
+		;
+
 tcpip		: TCP '{' optnl tcpflags_l '}'
 		| TCP tcpflags
 		;
@@ -1457,6 +1468,7 @@ lookup(char *s)
 		{ "body",		BODY },
 		{ "buffer",		BUFFER },
 		{ "ca",			CA },
+		{ "cache",		CACHE },
 		{ "certificate",	CERTIFICATE },
 		{ "chroot",		CHROOT },
 		{ "ciphers",		CIPHERS },
Index: server_http.c
===================================================================
RCS file: /home/cvs/src/usr.sbin/httpd/server_http.c,v
diff -u -p -r1.161 server_http.c
--- server_http.c	2 Mar 2026 19:24:58 -0000	1.161
+++ server_http.c	5 May 2026 19:36:23 -0000
@@ -1608,6 +1608,16 @@ server_response_http(struct client *clt,
 	    kv_set(cl, "%lld", (long long)size) == -1))
 		return (-1);
 
+	if (srv_conf->flags & SRVFLAG_CACHE) {
+		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)

-- 
wbr, Kirill