diff -rNu thttpd-2.22beta4/README.caching_headers thttpd-2.22beta4-jer/README.caching_headers
--- thttpd-2.22beta4/README.caching_headers	Wed Dec 31 16:00:00 1969
+++ thttpd-2.22beta4-jer/README.caching_headers	Fri Mar  8 15:35:02 2002
@@ -0,0 +1,20 @@
+Thanks to Jef Poskanzer for providing us this great piece of software!
+
+Added to thttpd/2.22beta4 (may show up in later release)
+
+* Jeremy Brand <jeremy@nirvani.net> 2002-03-08
+  http://www.jeremybrand.com/Jeremy/Brand/Jeremy_Brand.html 
+
+Added '-ch', '-noch' command line, and 'cachingheaders', 'nocachingheaders'
+config file options .  '-ch' (cachingheaders) supports Etag/If-None-Match
+headers (304 response).  Unless either '-ch' or '-noch' is specified (or the
+config file equivilants), only stock thttpd headers are sent.  Don't confuse
+'-noch' with not doing anything. '-noch' sends headers, just different kinds of
+headers than '-ch', the kind of headers that tell browsers and proxies "do no
+cache this page".  Similarly the '-ch' sends headers that tell proxies and
+browsers "feel free to cache this page".  '-ch' is great for saving money if
+you have to pay for bandwidth depending on usage.  '-noch' is great for
+ensuring your page doesn't get stuck in a proxy (AOL is famous for this) or in
+browser cache.  Stock thttpd headers are a nice medium.
+  
+
diff -rNu thttpd-2.22beta4/libhttpd.c thttpd-2.22beta4-jer/libhttpd.c
--- thttpd-2.22beta4/libhttpd.c	Wed Nov 14 14:32:12 2001
+++ thttpd-2.22beta4-jer/libhttpd.c	Fri Mar  8 16:04:21 2002
@@ -264,7 +266,8 @@
     char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P, int port,
     char* cgi_pattern, char* charset, char* p3p, char* cwd, int no_log,
     FILE* logfp, int no_symlink, int vhost, int global_passwd,
-    char* url_pattern, char* local_pattern, int no_empty_referers )
+    char* url_pattern, char* local_pattern, int no_empty_referers 
+    int caching_headers )
     {
     httpd_server* hs;
     static char ghnbuf[256];
@@ -373,6 +387,7 @@
     hs->vhost = vhost;
     hs->global_passwd = global_passwd;
     hs->no_empty_referers = no_empty_referers;
+    hs->caching_headers = caching_headers;
 
     /* Initialize listen sockets.  Try v6 first because of a Linux peculiarity;
     ** unlike other systems, it has magical v6 sockets that also listen for v4,
@@ -636,12 +651,15 @@
 send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod )
     {
     time_t now;
+    time_t fut;
     const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";
     char nowbuf[100];
     char modbuf[100];
+    char futbuf[100];
     char fixed_type[500];
     char buf[1000];
     int partial_content;
+    int max_age = 7776000;  /* 90 days in seconds */
 
     hc->status = status;
     hc->bytes_to_send = length;
@@ -668,10 +686,24 @@
 	(void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) );
 	(void) my_snprintf(
 	    fixed_type, sizeof(fixed_type), type, hc->hs->charset );
-	(void) my_snprintf( buf, sizeof(buf),
-	    "%.20s %d %s\r\nServer: %s\r\nContent-Type: %s\r\nDate: %s\r\nLast-Modified: %s\r\nAccept-Ranges: bytes\r\nConnection: close\r\n",
-	    hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type,
-	    nowbuf, modbuf );
+	switch(hc->hs->caching_headers)
+	{
+	    case 1: /* Headers that say "Cache me" */
+	    fut = (time_t)(now + max_age);
+	    (void) strftime( futbuf, sizeof(futbuf), rfc1123fmt, gmtime( &fut ) );
+
+	    (void) my_snprintf( buf, sizeof(buf), "%.20s %d %s\r\nServer: %s\r\nContent-Type: %s\r\nDate: %s\r\nLast-Modified: %s\r\nExpires: %s\r\nCache-Control: max-age=%d\r\nETag: %s\r\nAccept-Ranges: bytes\r\nConnection: close\r\n", hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type, nowbuf, modbuf, futbuf, max_age, hc->etag );
+	    break;
+
+	    case 0: /* Headers that say "Don't cache me" */
+	    (void) my_snprintf( buf, sizeof(buf), "%.20s %d %s\r\nServer: %s\r\nContent-Type: %s\r\nDate: %s\r\nLast-Modified: %s\r\nExpires: 0\r\nCache-Control: no-cache, must-revalidate\r\nPragma: no-cache\r\nAccept-Ranges: bytes\r\nConnection: close\r\n", hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type, nowbuf, nowbuf, futbuf );
+	    break;
+
+
+	    default:
+	    (void) my_snprintf( buf, sizeof(buf), "%.20s %d %s\r\nServer: %s\r\nContent-Type: %s\r\nDate: %s\r\nLast-Modified: %s\r\nAccept-Ranges: bytes\r\nConnection: close\r\n", hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type, nowbuf, modbuf );
+	    break;
+        }
 	add_response( hc, buf );
 	if ( encodings[0] != '\0' )
 	    {
@@ -1719,6 +1751,8 @@
     hc->query[0] = '\0';
     hc->referer = "";
     hc->useragent = "";
+    hc->ifnonematch = "";
+    hc->etag[0] = '\0';
     hc->accept[0] = '\0';
     hc->accepte[0] = '\0';
     hc->acceptl = "";
@@ -2049,6 +2083,12 @@
 		cp += strspn( cp, " \t" );
 		hc->useragent = cp;
 		}
+	    else if ( strncasecmp( buf, "If-None-Match:", 14 ) == 0 )
+		{
+		cp = &buf[14];
+		cp += strspn( cp, " \t" );
+		hc->ifnonematch = cp;
+		}
 	    else if ( strncasecmp( buf, "Host:", 5 ) == 0 )
 		{
 		cp = &buf[5];
@@ -2950,6 +2990,8 @@
 	envp[envn++] = build_env( "HTTP_REFERER=%s", hc->referer );
     if ( hc->useragent[0] != '\0' )
 	envp[envn++] = build_env( "HTTP_USER_AGENT=%s", hc->useragent );
+    if ( hc->ifnonematch[0] != '\0' )
+	envp[envn++] = build_env( "HTTP_IF_NONE_MATCH=%s", hc->ifnonematch);
     if ( hc->accept[0] != '\0' )
 	envp[envn++] = build_env( "HTTP_ACCEPT=%s", hc->accept );
     if ( hc->accepte[0] != '\0' )
@@ -3444,6 +3486,7 @@
     int expnlen, indxlen;
     char* cp;
     char* pi;
+    char etagbuf[50];
 
     expnlen = strlen( hc->expnfilename );
 
@@ -3651,6 +3694,7 @@
 	    hc->encodedurl );
 	return -1;
 	}
+
     if ( hc->pathinfo[0] != '\0' )
 	{
 	syslog(
@@ -3670,7 +3714,19 @@
 
     figure_mime( hc );
 
-    if ( hc->method == METHOD_HEAD )
+    my_snprintf( hc->etag, sizeof( hc->etag ), "\"%x-%x-%x\"", hc->sb.st_ino,
+        hc->sb.st_size, hc->sb.st_mtime );
+
+    /* rfc 2616 -- must send 304 on both HEAD and GET if etag exists in ifnonematch or if ifnonematch is '*' */
+    if ( ( hc->hs->caching_headers == 1 ) && ( hc->method == METHOD_HEAD || hc->method == METHOD_GET ) && 
+                  ( strcmp(hc->ifnonematch, "*") == 0 || strstr( hc->ifnonematch, hc->etag ) != NULL ) )
+	{
+	hc->method = METHOD_HEAD;
+	send_mime(
+	    hc, 304, err304title, hc->encodings, "", hc->type, hc->sb.st_size,
+	    hc->sb.st_mtime );
+	}
+    else if ( hc->method == METHOD_HEAD )
 	{
 	send_mime(
 	    hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,
@@ -3814,6 +3870,9 @@
     {
     int r;
     char* cp;
+    static char headstr[] = "Location: ";
+    static char* header;
+    static int maxlocation = 0, maxheader = 0;
 
     /* Are we doing referer checking at all? */
     if ( hc->hs->url_pattern == (char*) 0 )
diff -rNu thttpd-2.22beta4/libhttpd.h thttpd-2.22beta4-jer/libhttpd.h
--- thttpd-2.22beta4/libhttpd.h	Mon Nov 12 16:22:01 2001
+++ thttpd-2.22beta4-jer/libhttpd.h	Fri Mar  8 15:54:30 2002
@@ -79,8 +79,10 @@
     int vhost;
     int global_passwd;
     char* url_pattern;
     char* local_pattern;
     int no_empty_referers;
+    int caching_headers;
     } httpd_server;
 
 /* A connection. */
@@ -115,10 +117,12 @@
     char* hostdir;
     char* authorization;
     char* remoteuser;
+    char* ifnonematch;
+    char etag[50];
     char* response;
     int maxdecodedurl, maxorigfilename, maxexpnfilename, maxencodings,
-	maxpathinfo, maxquery, maxaccept, maxaccepte, maxreqhost, maxhostdir,
-	maxremoteuser, maxresponse;
+	maxpathinfo, maxquery, maxaccept, maxaccepte, maxreqhost, 
+        maxhostdir, maxremoteuser, maxresponse;
 #ifdef TILDE_MAP_2
     char* altdir;
     int maxaltdir;
@@ -169,7 +173,8 @@
     char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P, int port,
     char* cgi_pattern, char* charset, char* p3p, char* cwd, int no_log,
     FILE* logfp, int no_symlink, int vhost, int global_passwd,
-    char* url_pattern, char* local_pattern, int no_empty_referers );
+    char* url_pattern, char* local_pattern, int no_empty_referers
+    int caching_headers );
 
 /* Change the log file. */
 extern void httpd_set_logfp( httpd_server* hs, FILE* logfp );
diff -rNu thttpd-2.22beta4/thttpd.c thttpd-2.22beta4-jer/thttpd.c
--- thttpd-2.22beta4/thttpd.c	Wed Nov 14 14:32:27 2001
+++ thttpd-2.22beta4-jer/thttpd.c	Fri Mar  8 15:59:15 2002
@@ -80,6 +81,7 @@
 static char* user;
 static char* charset;
 static char* p3p;
+static int caching_headers;
 
 
 typedef struct {
@@ -437,8 +439,8 @@
 	hostname,
 	gotv4 ? &sa4 : (httpd_sockaddr*) 0, gotv6 ? &sa6 : (httpd_sockaddr*) 0,
 	port, cgi_pattern, charset, p3p, cwd, no_log, logfp, no_symlink,
	do_vhost, do_global_passwd, url_pattern, local_pattern,
-	no_empty_referers );
+	no_empty_referers, caching_headers );
     if ( hs == (httpd_server*) 0 )
 	exit( 1 );
 
@@ -623,6 +625,9 @@
 
     debug = 0;
     port = DEFAULT_PORT;
+    caching_headers = -1; /* 1 is caching headers, 0 is no caching headers, -1 is legacy thttpd, IE, do not involve any new headers */
+                          /* I see a future where this might change, and we have bitmasks fields for headers... and defines for     */
+                          /* caching and no caching.  Reason: why should I be the one who decides.. ;)  -Jeremy Brand               */
     dir = (char*) 0;
 #ifdef ALWAYS_CHROOT
     do_chroot = 1;
@@ -688,6 +694,10 @@
 	    no_symlink = 0;
 	else if ( strcmp( argv[argn], "-nos" ) == 0 )
 	    no_symlink = 1;
+	else if ( strcmp( argv[argn], "-ch" ) == 0 )
+            caching_headers = 1;
+        else if ( strcmp( argv[argn], "-noch" ) == 0 )
+            caching_headers = 0;
 	else if ( strcmp( argv[argn], "-u" ) == 0 && argn + 1 < argc )
 	    {
 	    ++argn;
@@ -848,6 +858,16 @@
 		no_value_required( name, value );
 		no_symlink = 1;
 		}
+	    else if ( strcasecmp( name, "nocachingheaders" ) == 0 )
+		{
+		no_value_required( name, value );
+		caching_headers = 0;
+		}
+	    else if ( strcasecmp( name, "cachingheaders" ) == 0 )
+		{
+		no_value_required( name, value );
+		caching_headers = 1;
+		}
 	    else if ( strcasecmp( name, "user" ) == 0 )
 		{
 		value_required( name, value );
