<p>I'm writing a resource handling method where I control access to various files, and I'd like to be able to make use of the browser's cache.  My question is two-fold:</p>

<ol>
<li><p>Which are the definitive HTTP headers that I need to check in order to know for sure whether I should send a 304 response, and what am I looking for when I do check them?</p></li>
<li><p>Additionally, are there any headers that I need to send when I initially send the file (like 'Last-Modified') as a 200 response?</p></li>
</ol>

<p>Some psuedo-code would probably be the most useful answer.</p>

<hr>

<p>What about the cache-control header?  Can the various possible values of that affect what you send to the client (namely max-age) or should only if-modified-since be obeyed?</p>

[{'answer_id': 4509, 'author': 'Ishmaeel', 'author_id': 227, 'author_profile': 'https://Stackoverflow.com/users/227', 'pm_score': 2, 'selected': False, 'text': '<p>You should send a 304 if the client has explicitly stated that it may already have the page in its cache. This is called a conditional GET, which should include the <strong>if-modified-since</strong> header in the request.</p>\n\n<p>Basically, this request header contains a date from which the client claims to have a cached copy. You should check if content has changed after this date and send a 304 if it hasn\'t.</p>\n\n<p>See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25" rel="nofollow noreferrer">http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25</a> for the related section in the RFC.</p>\n'}, {'answer_id': 4527, 'author': 'Mark Harrison', 'author_id': 116, 'author_profile': 'https://Stackoverflow.com/users/116', 'pm_score': 4, 'selected': True, 'text': '<p>Here\'s how I implemented it.  The code has been working for a bit more than a year and with multiple browsers, so I think it\'s pretty reliable.  This is based on <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html" rel="noreferrer">RFC 2616</a> and by observing what and when the various browsers were sending.</p>\n\n<p>Here\'s the pseudocode:</p>\n\n<pre>\nserver_etag = gen_etag_for_this_file(myfile)\netag_from_browser = get_header("Etag")\n\nif etag_from_browser does not exist:\n    etag_from_browser = get_header("If-None-Match")\nif the browser has quoted the etag:\n    strip the quotes (e.g. "foo" --> foo)\n\nset server_etag into http header\n\nif etag_from_browser matches server_etag\n    send 304 return code to browser\n</pre>\n\n<p>Here\'s a snippet of my server logic that handles this.</p>\n\n<pre>\n/* the client should set either Etag or If-None-Match */\n/* some clients quote the parm, strip quotes if so    */\nmketag(etag, &sb);\n\netagin = apr_table_get(r->headers_in, "Etag");\nif (etagin == NULL)\n    etagin = apr_table_get(r->headers_in, "If-None-Match");\nif (etag != NULL && etag[0] == \'"\') {\n    int sl; \n    sl = strlen(etag);\n    memmove(etag, etag+1, sl+1);\n    etag[sl-2] = 0;\n    logit(2,"etag=:%s:",etag);\n}   \n... \napr_table_add(r->headers_out, "ETag", etag);\n... \nif (etagin != NULL && strcmp(etagin, etag) == 0) {\n    /* if the etag matches, we return a 304 */\n    rc = HTTP_NOT_MODIFIED;\n}   \n</pre>\n\n<p>If you want some help with etag generation post another question and I\'ll dig out some code that does that as well.  HTH!</p>\n'}, {'answer_id': 4535, 'author': 'Mark Harrison', 'author_id': 116, 'author_profile': 'https://Stackoverflow.com/users/116', 'pm_score': 1, 'selected': False, 'text': "<p>regarding cache-control:</p>\n\n<p>You shouldn't have to worry about the cache-control when serving out, other than setting it to a reasonable value.  It's basically telling the browser and other downstream entities (such as a proxy) the maximum time that should elapse before timing out the cache.</p>\n"}, {'answer_id': 6768, 'author': 'Justin Sheehy', 'author_id': 11944, 'author_profile': 'https://Stackoverflow.com/users/11944', 'pm_score': 2, 'selected': False, 'text': '<p>A 304 Not Modified response can result from a GET or HEAD request with either an If-Modified-Since ("IMS") or an If-Not-Match ("INM") header.</p>\n\n<p>In order to decide what to do when you receive these headers, imagine that you are handling the GET request without these conditional headers.  Determine what the values of your ETag and Last-Modified headers would be in that response and use them to make the decision.  Hopefully you have built your system such that determining this is less costly than constructing the complete response.</p>\n\n<p>If there is an INM and the value of that header is the same as the value you would place in the ETag, then respond with 304.</p>\n\n<p>If there is an IMS and the date value in that header is later than the one you would place in the Last-Modified, then respond with 304.</p>\n\n<p>Else, proceed as though the request did not contain those headers.</p>\n\n<p>For a least-effort approach to part 2 of your question, figure out which of the (Expires, ETag, and Last-Modified) headers you can easily and correctly produce in your Web application. </p>\n\n<p>For suggested reading material:</p>\n\n<p><a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html" rel="nofollow noreferrer">http://www.w3.org/Protocols/rfc2616/rfc2616.html</a></p>\n\n<p><a href="http://www.mnot.net/cache_docs/" rel="nofollow noreferrer">http://www.mnot.net/cache_docs/</a></p>\n'}, {'answer_id': 1560461, 'author': 'Thomas S. Trias', 'author_id': 189048, 'author_profile': 'https://Stackoverflow.com/users/189048', 'pm_score': 2, 'selected': False, 'text': '<p>We are also handling cached, but secured, resources. \xa0If you send / generate an ETAg header (which RFC 2616 section 13.3 recommends you SHOULD), then the client MUST use it in a conditional request (typically in an If-None-Match - HTTP_IF_NONE_MATCH - header). \xa0If you send a Last-Modified header (again you SHOULD), then you should check the If-Modified-Since - HTTP_IF_MODIFIED_SINCE - header. \xa0If you send both, then the client SHOULD send both, but it MUST send the ETag. \xa0Also note that validtion is just defined as checking the conditional headers for strict equality against the ones you would send out. \xa0Also, only a strong validator (such as an ETag) will be used for ranged requests (where only part of a resource is requested).</p>\n\n<p>In practice, since the resources we are protecting are fairly static, and a one second lag time is acceptable, we are doing the following:</p>\n\n<ol>\n<li><p>\xa0Check to see if the user is authorized to access the requested resource</p>\n\n<p>\xa0\xa0 \xa0 If they are not, Redirect them or send a 4xx response as appropriate. \xa0We will generate 404 responses to requests that look like hack attempts or blatant tries to perform a security end run.</p></li>\n<li><p>\xa0Compare the If-Modified-Since header to the Last-Modified header we would send (see below) for strict equality</p>\n\n<p>\xa0\xa0 \xa0 If they match, send a 304 Not Modified response and exit page processing</p></li>\n<li><p>\xa0Create a Last-Modified header using the modification time of the requested resource</p>\n\n<p>\xa0\xa0 \xa0Look up the HTTP Date format in RFC 2616</p></li>\n<li><p>\xa0Send out the header and resource content along with an appropriate Content-Type</p></li>\n</ol>\n\n<p>We decided to eschew the ETag header since it is overkill for our purposes. \xa0I suppose we could also just use the date time stamp as an ETag. \xa0If we move to a true ETag system, we would probably store computed hashes for the resources and use those as ETags.</p>\n\n<p>If your resources are dynamically generated, from say database content, then ETags may be better for your needs, since they are just text to be populated as you see fit.</p>\n'}]
['https://Stackoverflow.com/questions/4506', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/192/']