Между прочим, это очень хороший вопрос.
В HTTP/1.0 Content-Length был совершенно необязателен - там на один запрос одно соединение. Нет Content-Length - значит, до опупения, то есть пока соединение не закрыто.
В HTTP/1.1 появился keep-alive, и тут возникла проблема, что делать, если Content-Length узнать невозможно. Обязать все бэкенды считать Content-Length и лишиться возможности отдавать поток "как есть" - плохая идея. Но когда закончилась передача ответа на данный запрос, и можно переходить к следующему, надо знать, иначе какой-то keep alive неправильный получается. Для этого придумали transfer-encoding: chunked, который помимо прочего прекрасно ложился на существующие архитектуры веб-серверов (в широком смысле). Вот есть буфер, отдаем его длину и содержимое (то есть чанк), и так по кругу, как поток закончился - отдаем нулевой чанк (длина 0).
Кстати, то, что php-прогромизды не понимают, что они пишут, часто выходит боком. Есть известный прикол со схемой nginx-apache+mod_php и всяким говнокодом вида <? header('HTTP/1.1 404 Not found'); readfile('404.tpl'); ?>. Nginx работает с бэкендом только по протоколу http/1.0 (почему так - отдельный вопрос, не суть). Заголовок с ответом 1.1 же "переключает" апач в работу по протоколу 1.1, и он честно отдает этот самый 404.tpl в chunked transfer encoding (а что ему еще делать если content-length никто не сказал?). Nginx же это дело забирает, ожидая ответа по 1.0 (а почему он должен ждать чего то еще если сделал запрос по 1.0?), и честно отдает все клиенту вместе с длинами чанков. А потом эти придурки бегут на форумы и спрашивают что за непонятные байты внезапно вылезли на хостинге и орут что хостинг дерьмо, ведь на денвере у них все работает.
