In PHP, what happens when file_get_contents() is given an https:// URL, and what does the allow_url_fopen setting control?
answer
- scheme:// picks a stream wrapper
- stream_get_wrappers() lists them
- allow_url_fopen: On by default, PHP_INI_SYSTEM
- http, https, ftp and data are URL wrappers
- false plus a warning when disabled
basics
~20 sThe https:// prefix selects PHP's HTTP stream wrapper, which sends a GET request and returns the body. allow_url_fopen, On by default and changeable only in system configuration, decides whether URL wrappers such as http://, https://, ftp:// and data:// may be opened.
solid answer
~50 sEvery PHP file function goes through a **stream wrapper**, chosen by the `scheme://` at the start of the path: no scheme means `file://`, `https://` means the HTTP wrapper (with OpenSSL loaded), `compress.zlib://` means the zlib wrapper, and `stream_get_wrappers()` lists what is registered. So `file_get_contents('https://…')` performs an HTTP GET and returns the response body as a string, or `false` with a warning on a network error or an error status. `allow_url_fopen` decides whether wrappers flagged as URLs (`http`, `https`, `ftp`, `data`) may be opened at all. It defaults to On, is On in both shipped php.ini files, and is `PHP_INI_SYSTEM`, so `ini_set()` cannot change it at runtime. With it Off, the call returns `false` and warns that the wrapper is disabled by `allow_url_fopen=0`. It does not affect `php://` or `compress.zlib://`, and it is separate from `allow_url_include`.
go deeper
Recall that a scheme:// prefix selects a stream wrapper and that allow_url_fopen gates remote URL wrappers.
Explain which wrappers count as URLs, why allow_url_fopen is PHP_INI_SYSTEM, and how errors and timeouts surface from file_get_contents().
Show you turn URL wrappers off where the code does not need them, and still treat php://, compress.zlib:// and phar:// as reachable from user-controlled paths.
Set a platform policy: which services may make outbound calls from PHP at all, and through which audited client, instead of relying on per-file conventions.
## Stream wrappers: one API, many back ends PHP's file functions (`fopen()`, `file_get_contents()`, `file()`, `copy()`, `readfile()`, `file_put_contents()` and more) do not talk to the disk directly. They hand the path to the **streams layer**, which picks a **wrapper** based on the start of the string: | Path starts with | Wrapper | What it reaches | |---|---|---| | no scheme, or `file://` | plain files | the local file system | | `http://`, `https://` | HTTP | a remote web server (`https` needs the OpenSSL extension) | | `ftp://`, `ftps://` | FTP | a remote FTP server | | `data:` | RFC 2397 data | bytes embedded in the URL itself | | `php://` | PHP I/O | memory/temp buffers, output, filters and more | | `compress.zlib://` | zlib | a gzip-compressed file (zlib extension) | | `phar://` | Phar | files inside a PHP archive | `stream_get_wrappers()` returns the list registered in the running PHP. The scheme is only recognised at the **very start** of the string; `/var/data/http://x` is an ordinary local path. ## What file_get_contents('https://…') does 1. The HTTP wrapper connects, sends a `GET` with PHP's default headers (including the `user_agent` ini value if set), and follows redirects. 2. On a 2xx response it returns the body as a string. 3. On a 4xx or 5xx response it returns `false` and emits a warning containing *HTTP request failed!*, unless a stream context sets `ignore_errors`. 4. It waits for data at most `default_socket_timeout` seconds at a time (60 by default) unless a context sets `timeout`. It is a quick way to fetch a URL, but it gives little control: no retries, limited error detail, blocking I/O. Production HTTP calls usually go through cURL or an HTTP client library. ## What allow_url_fopen controls Each wrapper carries an *is URL* flag. In php-src, the HTTP, FTP and `data:` wrappers set it; the plain-file, `php://`, `compress.zlib://` and `phar://` wrappers do not. When a script opens a wrapper with that flag: - if **`allow_url_fopen`** is Off, the open fails: the function returns `false` and warns *https:// wrapper is disabled in the server configuration by allow_url_fopen=0*; - if the open is for `include`/`require`, **`allow_url_include`** (Off by default) must also be On; that include-side setting is a security topic of its own. Facts about the setting: - Built-in default `1` (On), and `On` in both `php.ini-production` and `php.ini-development`. - Changeable only at the `PHP_INI_SYSTEM` level: php.ini, the web server or FPM pool configuration. `ini_set('allow_url_fopen', '0')` in a script fails and returns `false`. - It affects every function that opens through the streams layer, not only `fopen()`, despite the name. ## Why teams turn it off - A path parameter that reaches a file function can become an outbound request to an internal service when URL wrappers are enabled. - Remote calls hidden in innocent-looking file code are hard to find in review and impossible to time out well. - `data:` URLs let a caller feed arbitrary bytes where a file was expected. Turning it off forces remote access through an explicit HTTP client. It is not a complete defence: `php://filter`, `compress.zlib://` and `phar://` stay available, so user input must still never choose a path freely. ## Diagnosing a failed URL read When `file_get_contents('https://…')` returns `false`, the warning text names the cause: - *wrapper is disabled in the server configuration by allow_url_fopen=0*: the setting is Off for this SAPI or pool. - *Unable to find the wrapper "https" - did you forget to enable it when you configured PHP?*: the OpenSSL extension is not loaded, so `https` is not registered. - *HTTP request failed!* followed by a status line: the server answered with an error status. - A connection or timeout message: DNS, network or firewall trouble. `error_get_last()` returns the last error as an array, which lets code log the message without displaying it. ## Quick check ```php <?php var_dump(ini_get('allow_url_fopen')); // "1" by default var_dump(in_array('https', stream_get_wrappers(), true)); ```
- Can a script turn allow_url_fopen off for itself with ini_set()?No. The directive is registered as `PHP_INI_SYSTEM`, so it can only be set in php.ini, the web server configuration or an FPM pool file. `ini_set('allow_url_fopen', '0')` returns `false` and changes nothing. To restrict one application, set it in that application's pool or virtual-host configuration.
- Does allow_url_fopen = Off stop php://filter or compress.zlib:// paths?No. Only wrappers flagged as URLs are gated: HTTP, HTTPS, FTP and `data:`. `php://`, `compress.zlib://` and `phar://` are local wrappers and still open, so a path taken from user input can still pick them. Building paths from a fixed absolute base, or unregistering unused wrappers, is still needed.
- What does file_get_contents() return for a 404 from an https:// URL?By default `false`, with a warning whose text includes *HTTP request failed!* and the status line; the body is discarded. Setting the HTTP context option `ignore_errors` to `true` makes it return the error body instead, and the status is then read from the response headers.
saying these in an interview costs you the question
- allow_url_fopen only affects the fopen() function itself.
- allow_url_fopen can be switched off per request with ini_set().
- With allow_url_fopen Off, every wrapper except file:// is blocked.
- file_get_contents() returns the error page body for a 404 by default.
- PHP decides a path is a URL if it contains :// anywhere.