March 10th, 2010

Disable mod_deflate on a per-script basis

I recently migrated an old Perl CGI application from one server (running FreeBSD 4 and Apache 1) to another (running a modern-ish Linux and Apache 2).

Things mostly worked, but the new server had mod_deflate enabled by default, and it caused some trouble. Scripts that displayed incremental output were showing nothing for a while and then everything all at once, and it confused people into thinking the scripts weren't responding at all.

mod_deflate buffers up all script output and compresses it before sending it all at once. You can disable that by content type or by custom server configuration on a Location or Directory or VirtualHost basis.

There's another trick, though, to do it on a per-script basis, with no need to mess with the server configuration or HUP Apache. Anything that needs to display incremental output can send an additional header to do it:

Content-range: bytes */*

This is basically the same as saying there's no specific content-range, but it will make mod_deflate bail out per this code in mod_deflate.c:

        /* We can't operate on Content-Ranges */
        if (apr_table_get(r->headers_out, "Content-Range") != NULL) {
            ap_remove_output_filter(f);
            return ap_pass_brigade(f->next, bb);
        }

I was able to add a little bit of logic to the shared HTTP-header-output function to check for $| and add the content-range header to disable gzip content encoding. Works pretty well.

(I wasn't able to Google up any answers to this problem, so hopefully this helps someone down the line who needs the same thing.)