Skip to content
PHP News
Search
Implemented PHP 8.6

Limit maximum number of filter chains

Limits php://filter URLs to 16 chained filters by default, blocking attacks that use long chains to turn file reads into code execution.

Implement a limit on the maximum number of filters as outlined in the RFC?

Primary vote · 2/3 majority

30 Yes 0 No 2 abstain 100% approval

This poll has closed.

It passed unanimously with 30 votes in favor, none against and 2 abstaining, well above the two-thirds it needed. Voting closed on June 26, 2026, and the limit is implemented in PHP 8.6.

Summary

A php://filter URL lets you run a stream through filters, like base64 or rot13, as you read it, and you can chain many filters together with |. Attackers have learned to chain dozens or even hundreds of them. With enough filters, they can turn any file, even an empty one, into PHP code they control. This RFC from Sjoerd Langkemper caps how many filters a single php://filter URL can use, with a default limit of 16.

Why it helps

Long filter chains show up in real attacks:

  • Running code. If an app does include $_GET['a'], a filter chain can make it execute arbitrary code, not just files already on the server.
  • Reading files. With file($_GET['a']), errors triggered by the chain can leak a file's contents even when the app never displays them.
  • Faking file types. A chain can make /etc/passwd look enough like an image to pass a type check.

The RFC lists how many filters each technique needs. A basic error-based trick takes 5, leaking a whole file takes more than 50, and building a web shell takes more than 100. A search of open-source code found that normal apps use 1 filter, and occasionally 2.

How it looks

Short chains still work:

var_dump(file_get_contents("php://filter/string.toupper|string.rot13/resource=data://text/plain,hello"));
// string(5) "URYYB"

In PHP 8.6, going over 16 filters triggers a deprecation warning. If you set the limit yourself, going over it fails:

$ctx = stream_context_create(['filter' => ['max_filter_count' => 1]]);
var_dump(file_get_contents("php://filter/string.toupper|string.rot13/resource=data://text/plain,hello", false, $ctx));
// Warning: ... Failed to open stream: too many filters in ...
// bool(false)

You can raise the limit globally with stream_context_set_default(), but there's no php.ini setting for it. You can also attach filters one at a time with stream_filter_append(), which has no limit.

What it means for existing code

Most apps won't notice. If you chain more than 16 filters in one URL, you'll see a deprecation warning in PHP 8.6, and the RFC plans to make it an error in PHP 8.7. To keep that code working, set filter.max_filter_count in a stream context or switch to stream_filter_append(). Older PHP versions ignore the option, so it's safe to add now.