For CDN purposes I am collecting the modified times of various pieces on the page and want to set the Last-Modified header to them. What's the latest during a request a Drupal 7 module can call drupal_add_http_header?

I know the answer but I think this is a very interesting question and I somehow doubt most people would know the answer, so I will leave this open for a few days and then post the answer.


To avoid insane answers like using runkit or doing your own request cycle, I will say the answer is userland PHP only, and less than 10 line of code.


Headers can be sent anytime up until the content is sent to the page which happens in drupal_page_footer(). The whole page is output buffered: _drupal_bootstrap_page_header has the ob_start and drupal_page_footer has the ob_flush.

Headers are also overwritten by default so there is no reason why you can't re-send the Last-Modified header at any point.

So, how about hook_exit() (called from drupal_page_footer just before the ob_flush).

  • Heh. I tried to be too clever. You are right, of course, added a little explanation on why the print in drupal_deliver_html_page doesn't start the output. – user49 Oct 19 '15 at 16:11

Use hook_page_alter() to add an #attached property to the $page renderable array, this will be executed by drupal_process_attached() at the end of drupal_render(), which is called at the end of drupal_render_page(), which is called near the end of drupal_deliver_html_page().

drupal_page_footer() is called after drupal_render_page(), but since the output of drupal_render_page() is printed, that would be too late.


function example_page_alter(&$page) {
  $page['#attached']['drupal_add_http_header'][] = array('Last-Modified', 'Wed, 15 Nov 1995 04:58:08 GMT');
  • This is the one I thought of. Small note: it needs to be $page['#attached']['drupal_add_http_header'][] you left out the []. It's foreach ($elements['#attached'][$callback] as $args) { in drupal_process_attached. – user49 Oct 19 '15 at 16:08

If the module provides the page delivery call back, then the module should be able to inject the header at any point up until the callback calls drupal_get_http_header().

My solution was posted before the edit was added. In terms of being possible to do it 10 LoC, how is 5?

function mymodule_page_delivery_callback_alter(&$delivery_callback) {
  $delivery_callback = $default_delivery_callback == 'drupal_deliver_html_page'? 'drupal_deliver_html_page' : 'mymodule_page_delivery_callback';

function mymodule_page_delivery_callback($page_callback_result) {
  drupal_add_http_header('X-Example', 'skwashd showing off');
  • As the edit says, don't do your own request cycle. If you do this and another module comes along, do you duke it out who wins the delivery callback? And I doubt this is doable in 10 LoC anyways :) – user49 Oct 19 '15 at 8:31
  • 1
    Well, then this code is definitely not correct, a great many things will happen after this. Stay tuned! It's great to see it, however, it will help explaining things (and I will show how to do the delivery callback version correctly). – user49 Oct 19 '15 at 11:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy