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.

3 Answers 3


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, 2015 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, 2015 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, 2015 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, 2015 at 11:06

Your Answer

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