[php] PHP "php://input" vs $_POST

I have been directed to use the method php://input instead of $_POST when interacting with Ajax requests from JQuery. What I do not understand is the benefits of using this vs the global method of $_POST or $_GET.

This question is related to php ajax input

The answer is


The reason is that php://input returns all the raw data after the HTTP-headers of the request, regardless of the content type.

The PHP superglobal $_POST, only is supposed to wrap data that is either

  • application/x-www-form-urlencoded (standard content type for simple form-posts) or
  • multipart/form-data (mostly used for file uploads)

This is because these are the only content types that must be supported by user agents. So the server and PHP traditionally don't expect to receive any other content type (which doesn't mean they couldn't).

So, if you simply POST a good old HTML form, the request looks something like this:

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

But if you are working with Ajax a lot, this probaby also includes exchanging more complex data with types (string, int, bool) and structures (arrays, objects), so in most cases JSON is the best choice. But a request with a JSON-payload would look something like this:

POST /page.php HTTP/1.1

{"key1":"value1","key2":"value2","key3":"value3"}

The content would now be application/json (or at least none of the above mentioned), so PHP's $_POST-wrapper doesn't know how to handle that (yet).

The data is still there, you just can't access it through the wrapper. So you need to fetch it yourself in raw format with file_get_contents('php://input') (as long as it's not multipart/form-data-encoded).

This is also how you would access XML-data or any other non-standard content type.


php://input can give you the raw bytes of the data. This is useful if the POSTed data is a JSON encoded structure, which is often the case for an AJAX POST request.

Here's a function to do just that:

  /**
   * Returns the JSON encoded POST data, if any, as an object.
   * 
   * @return Object|null
   */
  private function retrieveJsonPostData()
  {
    // get the raw POST data
    $rawData = file_get_contents("php://input");

    // this returns null if not valid json
    return json_decode($rawData);
  }

The $_POST array is more useful when you're handling key-value data from a form, submitted by a traditional POST. This only works if the POSTed data is in a recognised format, usually application/x-www-form-urlencoded (see http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 for details).


First, a basic truth about PHP.

PHP was not designed to explicitly give you a pure REST (GET, POST, PUT, PATCH, DELETE) like interface for handling HTTP requests.

However, the $_SERVER, $_COOKIE, $_POST, $_GET, and $_FILES superglobals, and the function filter_input_array() are very useful for the average person's / layman's needs.

The number one hidden advantage of $_POST (and $_GET) is that your input data is url-decoded automatically by PHP. You never even think about having to do it, especially for query string parameters within a standard GET request, or HTTP body data submitted with a POST request.

Other HTTP Request Methods

Those studying the underlying HTTP protocol and its various request methods come to understand that there are many HTTP request methods, including the often referenced PUT, PATCH (not used in Google's Apigee), and DELETE.

In PHP, there are no superglobals or input filter functions for getting HTTP request body data when POST is not used. What are disciples of Roy Fielding to do? ;-)

However, then you learn more ...

That being said, as you advance in your PHP programming knowledge and want to use JavaScript's XmlHttpRequest object (jQuery for some), you come to see the limitation of this scheme.

$_POST limits you to the use of two media types in the HTTP Content-Type header:

  1. application/x-www-form-urlencoded, and
  2. multipart/form-data

Thus, if you want to send data values to PHP on the server, and have it show up in the $_POST superglobal, then you must urlencode it on the client-side and send said data as key/value pairs--an inconvenient step for novices (especially when trying to figure out if different parts of the URL require different forms of urlencoding: normal, raw, etc..).

For all you jQuery users, the $.ajax() method is converting your JSON to URL encoded key/value pairs before transmitting them to the server. You can override this behavior by setting processData: false. Just read the $.ajax() documentation, and don't forget to send the correct media type in the Content-Type header.

php://input, but ...

Even if you use php://input instead of $_POST for your HTTP POST request body data, it will not work with an HTTP Content-Type of multipart/form-data This is the content type that you use on an HTML form when you want to allow file uploads!

<form enctype="multipart/form-data" accept-charset="utf-8" action="post">
    <input type="file" name="resume">
</form>

Therefore, in traditional PHP, to deal with a diversity of content types from an HTTP POST request, you will learn to use $_POST or filter_input_array(POST), $_FILES, and php://input. There is no way to just use one, universal input source for HTTP POST requests in PHP.

You cannot get files through $_POST, filter_input_array(POST), or php://input, and you cannot get JSON/XML/YAML in either filter_input_array(POST) or $_POST.

PHP Manual: php://input

php://input is a read-only stream that allows you to read raw data from the request body...php://input is not available with enctype="multipart/form-data".

PHP Frameworks to the rescue?

PHP frameworks like Codeigniter 4 and Laravel use a facade to provide a cleaner interface (IncomingRequest or Request objects) to the above. This is why professional PHP developers use frameworks instead of raw PHP.

Of course, if you like to program, you can devise your own facade object to provide what frameworks do. It is because I have taken time to investigate this issue that I am able to write this answer.

URL encoding? What the heck!!!???

Typically, if you are doing a normal, synchronous (when the entire page redraws) HTTP requests with an HTML form, the user-agent (web browser) will urlencode your form data for you. If you want to do an asynchronous HTTP requests using the XmlHttpRequest object, then you must fashion a urlencoded string and send it, if you want that data to show up in the $_POST superglobal.

How in touch are you with JavaScript? :-)

Converting from a JavaScript array or object to a urlencoded string bothers many developers (even with new APIs like Form Data). They would much rather just be able to send JSON, and it would be more efficient for the client code to do so.

Remember (wink, wink), the average web developer does not learn to use the XmlHttpRequest object directly, global functions, string functions, array functions, and regular expressions like you and I ;-). Urlencoding for them is a nightmare. ;-)

PHP, what gives?

PHP's lack of intuitive XML and JSON handling turns many people off. You would think it would be part of PHP by now (sigh).

So many media types (MIME types in the past)

XML, JSON, and YAML all have media types that can be put into an HTTP Content-Type header.

  • application/xml
  • applicaiton/json
  • application/yaml (although IANA has no official designation listed)

Look how many media-types (formerly, MIME types) are defined by IANA.

Look how many HTTP headers there are.

php://input or bust

Using the php://input stream allows you to circumvent the baby-sitting / hand holding level of abstraction that PHP has forced on the world. :-) With great power comes great responsibility!

Now, before you deal with data values streamed through php://input, you should / must do a few things.

  1. Determine if the correct HTTP method has been indicated (GET, POST, PUT, PATCH, DELETE, ...)
  2. Determine if the HTTP Content-Type header has been transmitted.
  3. Determine if the value for the Content-Type is the desired media type.
  4. Determine if the data sent is well formed XML / JSON / YAML / etc.
  5. If necessary, convert the data to a PHP datatype: array or object.
  6. If any of these basic checks or conversions fails, throw an exception!

What about the character encoding?

AH, HA! Yes, you might want the data stream being sent into your application to be UTF-8 encoded, but how can you know if it is or not?

Two critical problems.

  1. You do not know how much data is coming through php://input.
  2. You do not know for certain the current encoding of the data stream.

Are you going to attempt to handle stream data without knowing how much is there first? That is a terrible idea. You cannot rely exclusively on the HTTP Content-Length header for guidance on the size of streamed input because it can be spoofed.

You are going to need a:

  1. Stream size detection algorithm.
  2. Application defined stream size limits (Apache / Nginx / PHP limits may be too broad).

Are you going to attempt to convert stream data to UTF-8 without knowing the current encoding of the stream? How? The iconv stream filter (iconv stream filter example) seems to want a starting and ending encoding, like this.

'convert.iconv.ISO-8859-1/UTF-8'

Thus, if you are conscientious, you will need:

  1. Stream encoding detection algorithm.
  2. Dynamic / runtime stream filter definition algorithm (because you cannot know the starting encoding a priori).

(Update: 'convert.iconv.UTF-8/UTF-8' will force everything to UTF-8, but you still have to account for characters that the iconv library might not know how to translate. In other words, you have to some how define what action to take when a character cannot be translated: 1) Insert a dummy character, 2) Fail / throw and exception).

You cannot rely exclusively on the HTTP Content-Encoding header, as this might indicate something like compression as in the following. This is not what you want to make a decision off of in regards to iconv.

Content-Encoding: gzip

Therefore, the general steps might be ...

Part I: HTTP Request Related

  1. Determine if the correct HTTP method has been indicated (GET, POST, PUT, PATCH, DELETE, ...)
  2. Determine if the HTTP Content-Type header has been transmitted.
  3. Determine if the value for the Content-Type is the desired media type.

Part II: Stream Data Related

  1. Determine the size of the input stream (optional, but recommended).
  2. Determine the encoding of the input stream.
  3. If necessary, convert the input stream to the desired character encoding (UTF-8).
  4. If necessary, reverse any application level compression or encryption, and then repeat steps 4, 5, and 6.

Part III: Data Type Related

  1. Determine if the data sent is well formed XML / JSON / YMAL / etc.

(Remember, the data can still be a URL encoded string which you must then parse and URL decode).

  1. If necessary, convert the data to a PHP datatype: array or object.

Part IV: Data Value Related

  1. Filter input data.

  2. Validate input data.

Now do you see?

The $_POST superglobal, along with php.ini settings for limits on input, are simpler for the layman. However, dealing with character encoding is much more intuitive and efficient when using streams because there is no need to loop through superglobals (or arrays, generally) to check input values for the proper encoding.


If post data is malformed, $_POST will not contain anything. Yet, php://input will have the malformed string.

For example there is some ajax applications, that do not form correct post key-value sequence for uploading a file, and just dump all the file as post data, without variable names or anything. $_POST will be empty, $_FILES empty also, and php://input will contain exact file, written as a string.


So I wrote a function that would get the POST data from the php://input stream.

So the challenge here was switching to PUT, DELETE OR PATCH request method, and still obtain the post data that was sent with that request.

I'm sharing this maybe for someone with a similar challenge. The function below is what I came up with and it works. I hope it helps!

    /**
     * @method Post getPostData
     * @return array
     * 
     * Convert Content-Disposition to a post data
     */
    function getPostData() : array
    {
        // @var string $input
        $input = file_get_contents('php://input');

        // continue if $_POST is empty
        if (strlen($input) > 0 && count($_POST) == 0 || count($_POST) > 0) :

            $postsize = "---".sha1(strlen($input))."---";

            preg_match_all('/([-]{2,})([^\s]+)[\n|\s]{0,}/', $input, $match);

            // update input
            if (count($match) > 0) $input = preg_replace('/([-]{2,})([^\s]+)[\n|\s]{0,}/', '', $input);

            // extract the content-disposition
            preg_match_all("/(Content-Disposition: form-data; name=)+(.*)/m", $input, $matches);

            // let's get the keys
            if (count($matches) > 0 && count($matches[0]) > 0)
            {
                $keys = $matches[2];

                foreach ($keys as $index => $key) :
                    $key = trim($key);
                    $key = preg_replace('/^["]/','',$key);
                    $key = preg_replace('/["]$/','',$key);
                    $key = preg_replace('/[\s]/','',$key);
                    $keys[$index] = $key;
                endforeach;

                $input = preg_replace("/(Content-Disposition: form-data; name=)+(.*)/m", $postsize, $input);

                $input = preg_replace("/(Content-Length: )+([^\n]+)/im", '', $input);

                // now let's get key value
                $inputArr = explode($postsize, $input);

                // @var array $values
                $values = [];

                foreach ($inputArr as $index => $val) :
                    $val = preg_replace('/[\n]/','',$val);

                    if (preg_match('/[\S]/', $val)) $values[$index] = trim($val);

                endforeach;

                // now combine the key to the values
                $post = [];

                // @var array $value
                $value = [];

                // update value
                foreach ($values as $i => $val) $value[] = $val;

                // push to post
                foreach ($keys as $x => $key) $post[$key] = isset($value[$x]) ? $value[$x] : '';

                if (is_array($post)) :

                    $newPost = [];

                    foreach ($post as $key => $val) :

                        if (preg_match('/[\[]/', $key)) :

                            $k = substr($key, 0, strpos($key, '['));
                            $child = substr($key, strpos($key, '['));
                            $child = preg_replace('/[\[|\]]/','', $child);
                            $newPost[$k][$child] = $val;

                        else:

                            $newPost[$key] = $val;

                        endif;

                    endforeach;

                    $_POST = count($newPost) > 0 ? $newPost : $post;

                endif;
            }

        endif;

        // return post array
        return $_POST;
    }

Simple example of how to use it

 <?php  
     if(!isset($_POST) || empty($_POST)) { 
     ?> 
        <form name="form1" method="post" action=""> 
          <input type="text" name="textfield"><br /> 
          <input type="submit" name="Submit" value="submit"> 
        </form> 
   <?php  
        } else { 
        $example = file_get_contents("php://input");
        echo $example;  }  
   ?>

Questions with php tag:

I am receiving warning in Facebook Application using PHP SDK Pass PDO prepared statement to variables Parse error: syntax error, unexpected [ Preg_match backtrack error Removing "http://" from a string How do I hide the PHP explode delimiter from submitted form results? Problems with installation of Google App Engine SDK for php in OS X Laravel 4 with Sentry 2 add user to a group on Registration php & mysql query not echoing in html with tags? How do I show a message in the foreach loop? Target class controller does not exist - Laravel 8 Message: Trying to access array offset on value of type null Array and string offset access syntax with curly braces is deprecated Visual Studio Code PHP Intelephense Keep Showing Not Necessary Error How to fix "set SameSite cookie to none" warning? The POST method is not supported for this route. Supported methods: GET, HEAD. Laravel Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.62.dylib error running php after installing node with brew on Mac What does double question mark (??) operator mean in PHP Post request in Laravel - Error - 419 Sorry, your session/ 419 your page has expired PHP with MySQL 8.0+ error: The server requested authentication method unknown to the client php mysqli_connect: authentication method unknown to the client [caching_sha2_password] Converting a POSTMAN request to Curl Composer require runs out of memory. PHP Fatal error: Allowed memory size of 1610612736 bytes exhausted Expected response code 250 but got code "530", with message "530 5.7.1 Authentication required Issue in installing php7.2-mcrypt Xampp localhost/dashboard How can I run specific migration in laravel How to change PHP version used by composer Laravel 5 show ErrorException file_put_contents failed to open stream: No such file or directory Artisan migrate could not find driver phpMyAdmin ERROR: mysqli_real_connect(): (HY000/1045): Access denied for user 'pma'@'localhost' (using password: NO) Ajax LARAVEL 419 POST error Laravel 5.5 ajax call 419 (unknown status) laravel 5.5 The page has expired due to inactivity. Please refresh and try again "The page has expired due to inactivity" - Laravel 5.5 How to increment a letter N times per iteration and store in an array? Can't install laravel installer via composer Only on Firefox "Loading failed for the <script> with source" Is there way to use two PHP versions in XAMPP? How to prevent page from reloading after form submit - JQuery laravel Eloquent ORM delete() method No Application Encryption Key Has Been Specified General error: 1364 Field 'user_id' doesn't have a default value How to logout and redirect to login page using Laravel 5.4? How to uninstall an older PHP version from centOS7 How to Install Font Awesome in Laravel Mix PDO::__construct(): Server sent charset (255) unknown to the client. Please, report to the developers Laravel - htmlspecialchars() expects parameter 1 to be string, object given How to downgrade php from 7.1.1 to 5.6 in xampp 7.1.1?

Questions with ajax tag:

Getting all files in directory with ajax Cross-Origin Read Blocking (CORB) Jquery AJAX: No 'Access-Control-Allow-Origin' header is present on the requested resource Fetch API request timeout? How do I post form data with fetch api? Ajax LARAVEL 419 POST error Laravel 5.5 ajax call 419 (unknown status) How to allow CORS in react.js? Angular 2: How to access an HTTP response body? How to post a file from a form with Axios console.log(result) returns [object Object]. How do I get result.name? $http.get(...).success is not a function What is difference between Axios and Fetch? Make XmlHttpRequest POST using JSON React Js: Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0 Response to preflight request doesn't pass access control check How can I send an Ajax Request on button click from a form with 2 buttons? API Gateway CORS: no 'Access-Control-Allow-Origin' header Download pdf file using jquery ajax Getting request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource AngularJS POST Fails: Response for preflight has invalid HTTP status code 404 Why my $.ajax showing "preflight is invalid redirect error"? "Mixed content blocked" when running an HTTP AJAX operation in an HTTPS page Jquery in React is not defined Laravel csrf token mismatch for ajax POST Request How to write data to a JSON file using Javascript loading json data from local file into React JS Load More Posts Ajax Button in WordPress jQuery ajax request being block because Cross-Origin CORS header 'Access-Control-Allow-Origin' missing How do I cancel an HTTP fetch() request? Send form data with jquery ajax json How to refresh table contents in div using jquery/ajax Ajax post request in laravel 5 return error 500 (Internal Server Error) React JS - Uncaught TypeError: this.props.data.map is not a function Uncaught TypeError: Cannot read property 'appendChild' of null Required request body content is missing: org.springframework.web.method.HandlerMethod$HandlerMethodParameter No 'Access-Control-Allow-Origin' header is present on the requested resource error Synchronous XMLHttpRequest warning and <script> React.js create loop through Array Reinitialize Slick js after successful ajax call jQuery has deprecated synchronous XMLHTTPRequest How to call a php script/function on a html button click jQuery Refresh/Reload Page if Ajax Success after time Solve Cross Origin Resource Sharing with Flask Stupid error: Failed to load resource: net::ERR_CACHE_MISS SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data Windows.history.back() + location.reload() jquery AJAX jQuery refresh div every 5 seconds Dynamically add item to jQuery Select2 control that uses AJAX

Questions with input tag:

Angular 4 - get input value React - clearing an input value after form submit Min and max value of input in angular4 application Disable Button in Angular 2 Angular2 - Input Field To Accept Only Numbers How to validate white spaces/empty spaces? [Angular 2] Can't bind to 'ngModel' since it isn't a known property of 'input' Mask for an Input to allow phone numbers? File upload from <input type="file"> Why does the html input with type "number" allow the letter 'e' to be entered in the field? HTML Input - already filled in text How to get multiline input from user Get input value from TextField in iOS alert in Swift HTML Input Type Date, Open Calendar by default Reading an integer from user input Why cannot change checkbox color whatever I do? How to properly validate input values with React.JS? How do I make an input field accept only letters in javaScript? How to read multiple Integer values from a single line of input in Java? Hide Spinner in Input Number - Firefox 29 How to take character input in java How to input matrix (2D list) in Python? Input type "number" won't resize Want to show/hide div based on dropdown box selection input() error - NameError: name '...' is not defined Get the value of input text when enter key pressed Play audio as microphone input How can I read inputs as numbers? How to force input to only allow Alpha Letters? Multiple input box excel VBA How can I get input radio elements to horizontally align? Is there a float input type in HTML5? how to customise input field width in bootstrap 3 Change input value onclick button - pure javascript or jQuery onchange event on input type=range is not triggering in firefox while dragging html form - make inputs appear on the same line What does "for" attribute do in HTML <label> tag? How to style HTML5 range input to have different color before and after slider? Java using scanner enter key pressed Styling input radio with css Check if input is number or letter javascript How do I check if the user is pressing a key? How to make a input field readonly with JavaScript? Detect key input in Python Image convert to Base64 How to change the text color of first select option Getting Keyboard Input jQuery 'input' event How to make <input type="file"/> accept only these types? HTML how to clear input using javascript?