Apache Ignores Content-Length and Uses Chunked Encoding

Apache Ignores Content-Length and Uses Chunked Encoding

If you have a server-side script (CGI, FastCGI, mod_cgid, or certain PHP configurations) that explicitly sets a Content-Length header, you might reasonably expect Apache to pass that value through unchanged so the client can read exactly that many bytes and stop. This was the normal behavior for Apache prior to version 2.4.59 (released in mid-2024). For applications using the lower-level SocketTools functions like HttpRead or InetRead, it made handling the response simpler because the server would tell you exactly how many bytes you needed to read.

When those same scripts are run on an updated version of Apache, the behavior changes to always return the output using chunked encoding, even when a content length is provided. In this case, the server responds with the Transfer-Encoding header set to chunked and there is no Content-Length header, even though your script set one.

Why this Changed

In newer versions of Apache, responses generated by "CGI-like" handlers are treated conservatively. By default, Apache does not trust a script-supplied Content-Length header. If Apache decides it cannot safely rely on the length provided by the script, it will ignore it and instead stream the response using chunked transfer encoding.

This behavior was introduced as a security hardening measure. Incorrect or malicious Content-Length values can cause request smuggling or response truncation issues if blindly trusted. If you're using the higher-level SocketTools functions like HttpGetData or HttpPostXml you may not have even noticed this change in behavior because the API automatically handles chunked encoding, returning the entire response payload with a single function call.  However, applications using a lower-level approach may receive what appears to be corrupted data without any content length specified.

How to Resolve the Problem

The change to always using chunked encoding may cause applications to fail or return what appears to be invalid data. Even worse, the application itself may not detect that chunk sizes are being included in the response payload. The response is stored as if it is valid and the problem is only discovered at a later time when the data is processed. There are a few options:

  1. Switch to using the higher-level SocketTools functions or methods
  2. Change your application to recognize chunked encoding in the response
  3. Configure Apache to trust the Content-Length header and use that value

The first two options are good long-term solutions but they require code changes and deploying updates to your application. Using the higher-level functions which automatically handle chunked encoding can simplify things; however, if your application was designed to expect to use lower-level socket I/O, that can also mean a significant implementation change. Likewise, adding code to recognize chunked output creates some additional complexity in your code.

The solution that offers the least friction and doesn't require any changes to your code is to tell Apache that it can trust the Content-Length value that your scripts are reporting. In this case, Apache will revert back to its previous behavior and will honor the content length provided rather than switching to chunked output.

Add the following directive to your Apache configuration file:

SetEnv ap_trust_cgilike_cl 1

Once this environment variable is set, Apache will allow CGI-like scripts to control Content-Length normally.

Although this can be done globally, we recommend that you scope this setting as narrowly as possible, enabling it only for scripts you control and trust. You can either scope it per-directory or for a specific URL path. If your scripts are all located under a single folder on the server, you can do this:

<Directory "/var/www/html/api">
    SetEnv ap_trust_cgilike_cl 1
    SetEnv no-gzip 1
    SetEnv dont-vary 1
</Directory>

If you prefer to specify a URL path instead, you can use this approach:

<Location "/api/">
    SetEnv ap_trust_cgilike_cl 1
    SetEnv no-gzip 1
    SetEnv dont-vary 1
</Location>

It's also possible to limit this to scripts which use a specific file extension or have a specific name. However, in most cases using the <Directory> configuration option is the simplest, most straight-foward approach.

In addition to telling Apache to trust the content length being set, this also explicitly disables compressed output. This is important because if mod_deflate or another output filter compresses the response after your script runs, the number of bytes sent over the network will no longer match the value you calculated. This can cause Apache to ignore the length and revert to chunked output or send an incorrect length.

In summary, these settings enable the following changes:

  • ap_trust_cgilike_cl tells Apache to trust the Content-Length header set by the script
  • no-gzip tells mod_deflate not to compress the response
  • dont-vary prevents Apache from adding Vary: Accept-Encoding, which can confuse some caches

In most cases, these changes would be made in the Apache configuration file, but this directive can also be placed in the .htaccess file if the server allows it. This would require the AllowOverride setting for the folder hosting the scripts to permit the change. If you add the SetEnv directives and there is no change in behavior, it either means:

  • The directive may be disallowed and silently ignored
  • The request may not be using the directory you expect (due to aliases, rewrites, etc.)

When in doubt, placing it in the virtual host configuration is the most reliable approach. Note that you would typically need administrator (root) level access to be able to make these changes and you will need to restart the server with the new configuration.

See Also

Sending and Receiving Binary Data

Shopping Cart
Scroll to Top