$_POST

Yo Yogesh.M Updated 01 Nov 2026
8 min read ·Lesson 216 of 219

What Is $_POST?

In simple terms: $_POST is an array containing the data a browser sends when a user submits a form with method="post". Each form field's name becomes a key, and what the user entered becomes the value. Technically: $_POST is a superglobal that PHP populates from the body of an HTTP request when the request's Content-Type is application/x-www-form-urlencoded (the default for HTML forms) or multipart/form-data (forms with file uploads). Other body formats, such as JSON, do not populate $_POST.

Why POST Is Used for Forms

GETPOST
Where data travelsIn the URLIn the request body
Visible in address bar, history, server logsYesNo
Can be bookmarked or shared as a linkYesNo
Typical size limitA few kilobytes (URL length)Configurable, megabytes (post_max_size)
Meaning"Fetch something" (safe, repeatable)"Do something" (create, update, delete, log in)

Use POST for anything that changes data or contains sensitive information (passwords, personal details), and for large inputs.


A Basic Form

subscribe.php

<?php
$message = '';
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
    $email = trim($_POST['email'] ?? '');
    if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
        $message = 'Please enter a valid email address.';
    } else {
        // In a real application: save to the database here
        $message = 'Subscribed: ' . htmlspecialchars($email);
    }
}
?>
<form method="post" action="">
    <label for="email">Email</label>
    <input type="email" id="email" name="email">
    <button type="submit">Subscribe</button>
</form>
<p><?= $message ?></p>

What Happens

  1. First visit (GET): the request method is GET, so the form is shown with an empty message.
  2. Submit: the browser sends a POST request whose body contains email=asha%40example.com.
  3. PHP decodes the body and fills $_POST: ['email' => 'asha@example.com'].
  4. The script validates the email and shows a message. The email is escaped with htmlspecialchars() before being printed.

Output After Submitting asha@example.com

<p>Subscribed: asha@example.com</p>

Key Points

  • The array key comes from the field's name attribute — not from id. A field without name is not sent.
  • action="" submits the form to the current URL.
  • $_SERVER['REQUEST_METHOD'] === 'POST' distinguishes "show the form" from "process the form".
  • ?? '' provides a default, because a manipulated request might not include the field.

Testing POST Requests With curl

You do not need a browser to send POST data. With the built-in server running (php -S localhost:8000):

curl -X POST -d "email=asha@example.com" http://localhost:8000/subscribe.php

-d sends form-encoded data in the body. This is also a reminder that anyone can send any POST data to your script, without using your form.


Form Field Types and $_POST

FieldWhat Is Sent
Text, email, password, textareaThe entered text (possibly an empty string)
SelectThe value of the chosen option
Radio buttons (same name)The value of the selected button; nothing if none selected
CheckboxIts value (default "on") if checked; nothing if unchecked
Disabled fieldsNothing
Submit button with a nameIts value, if that button was clicked

Because unchecked checkboxes and unselected radio buttons are not sent at all, always read them with a default:

$newsletter = isset($_POST['newsletter']);          // checkbox: true if checked
$plan = $_POST['plan'] ?? 'free';                    // radio: default if none chosen

Arrays From Form Fields

Field names with square brackets create arrays in $_POST:

<input type="checkbox" name="skills[]" value="php">
<input type="checkbox" name="skills[]" value="mysql">
<input type="text" name="address[city]">
<input type="text" name="address[pin]">

PHP parses these names exactly like parse_str() parses a query string, which lets you see the result without a browser:

<?php
$body = 'name=Asha&skills%5B%5D=php&skills%5B%5D=mysql&address%5Bcity%5D=Chennai&address%5Bpin%5D=600042';
parse_str($body, $data);
print_r($data);

Output

Array
(
    [name] => Asha
    [skills] => Array
        (
            [0] => php
            [1] => mysql
        )
    [address] => Array
        (
            [city] => Chennai
            [pin] => 600042
        )
)
  • skills[] (empty brackets) collects multiple values into a list — ideal for checkbox groups and multi-selects.
  • address[city] creates a nested associative array, which is useful for grouping related fields.
  • In the body, [ and ] are URL-encoded as %5B and %5D.

Every Field Can Be String or Array

Because the client chooses field names, a field you expect to be a string can arrive as an array (name[]=x), and vice versa. Passing an unexpected array to a string function throws a TypeError. A small helper prevents this:

<?php
function postString(string $key, string $default = ''): string
{
    $value = $_POST[$key] ?? $default;
    return is_string($value) ? trim($value) : $default;
}
function postArray(string $key): array
{
    $value = $_POST[$key] ?? [];
    return is_array($value) ? $value : [];
}

Practical Example: Contact Form With Validation and Redirect

contact.php

<?php
session_start();
function postString(string $key): string
{
    $value = $_POST[$key] ?? '';
    return is_string($value) ? trim($value) : '';
}
$errors = [];
$old = ['name' => '', 'email' => '', 'message' => ''];
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
    $old = [
        'name' => postString('name'),
        'email' => postString('email'),
        'message' => postString('message'),
    ];
    if ($old['name'] === '') {
        $errors['name'] = 'Please enter your name.';
    }
    if (filter_var($old['email'], FILTER_VALIDATE_EMAIL) === false) {
        $errors['email'] = 'Please enter a valid email address.';
    }
    if (mb_strlen($old['message']) < 10) {
        $errors['message'] = 'Message must be at least 10 characters.';
    }
    if ($errors === []) {
        // Save the message or send an email here.
        $_SESSION['flash'] = 'Thank you, ' . $old['name'] . '! We will reply soon.';
        header('Location: /contact.php', true, 303);
        exit;
    }
}
$flash = $_SESSION['flash'] ?? null;
unset($_SESSION['flash']);
function e(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
?>
<?php if ($flash !== null): ?>
<p class="success"><?= e($flash) ?></p>
<?php endif; ?>
<form method="post" action="/contact.php" novalidate>
    <label>Name <input name="name" value="<?= e($old['name']) ?>"></label>
    <?php if (isset($errors['name'])): ?><span class="error"><?= e($errors['name']) ?></span><?php endif; ?>
    <label>Email <input type="email" name="email" value="<?= e($old['email']) ?>"></label>
    <?php if (isset($errors['email'])): ?><span class="error"><?= e($errors['email']) ?></span><?php endif; ?>
    <label>Message <textarea name="message"><?= e($old['message']) ?></textarea></label>
    <?php if (isset($errors['message'])): ?><span class="error"><?= e($errors['message']) ?></span><?php endif; ?>
    <button type="submit">Send</button>
</form>

How It Behaves

ScenarioResult
First visitEmpty form
Submitted with errors (e.g. invalid email)Form shown again with the user's values filled in and an error next to each invalid field
Submitted successfullyBrowser is redirected to /contact.php, which shows "Thank you, Asha! We will reply soon."
Page refreshed after successThe message disappears; nothing is submitted again

Step-by-Step Explanation

  1. Reading input: postString() trims values and ignores unexpected arrays.
  2. Validation: each rule adds an error keyed by field name, so the template can show it next to the right field.
  3. Keeping user input: $old holds what the user typed, so the form can be refilled after an error — users should not have to retype everything.
  4. Post/Redirect/Get (PRG): after a successful submission, the script redirects with status 303 See Other, which tells the browser to load the page with a GET request. Refreshing the page then repeats the harmless GET, not the POST — preventing duplicate messages or orders.
  5. Flash message: the success message is stored in the session before the redirect and removed after it is displayed once (see PHP Sessions).
  6. Escaping: every value printed into HTML goes through e(), including the user's own input.
  7. novalidate turns off the browser's built-in validation so you can see the server-side checks working. Browser validation improves user experience, but server-side validation is always required, because requests can bypass the browser. A production form would also include a CSRF token (see CSRF Protection).

JSON Request Bodies

JavaScript applications and APIs often send JSON instead of form data:

curl -X POST -H "Content-Type: application/json" \
     -d '{"name": "Asha", "plan": "pro"}' \
     http://localhost:8000/api/subscribe.php

For such requests, $_POST is empty. Read the raw body instead:

<?php
// api/subscribe.php
header('Content-Type: application/json');
try {
    $data = json_decode(file_get_contents('php://input'), true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    http_response_code(400);
    echo json_encode(['error' => 'Invalid JSON']);
    exit;
}
$name = is_array($data) && is_string($data['name'] ?? null) ? trim($data['name']) : '';
if ($name === '') {
    http_response_code(422);
    echo json_encode(['error' => 'Name is required']);
    exit;
}
echo json_encode(['message' => "Welcome, $name"]);

Response for the curl Request Above

{"message":"Welcome, Asha"}
  • php://input is a read-only stream containing the raw request body.
  • JSON_THROW_ON_ERROR makes invalid JSON throw a JsonException, which is answered with 400 Bad Request.
  • Missing or invalid fields are answered with 422 Unprocessable Content, a common status for validation errors in APIs.

Size Limits

php.ini SettingDefaultEffect
post_max_size8MMaximum size of the entire POST body
max_input_vars1000Maximum number of input fields; extra fields are dropped with a warning
upload_max_filesize2MMaximum size per uploaded file (see PHP File Upload)

Important: When a request body exceeds post_max_size, PHP discards it: $_POST and $_FILES are empty, and a warning is logged. A form that "submits but loses all data" is often caused by this. Check $_SERVER['CONTENT_LENGTH'] against the limit to show a helpful error.


Common Mistakes

Missing name Attributes

Effect: Fields are not sent; $_POST lacks the expected keys. Fix: Give every input a name.

Reading Fields Without Defaults

Effect: Warnings for unchecked checkboxes and manipulated requests. Fix: Use ?? '', isset(), or helper functions.

Trusting Hidden Fields and Select Values

Effect: Users change prices, IDs, or roles in the browser's developer tools. Fix: Validate against allowed values and look up important data (prices, permissions) on the server.

Printing Input Unescaped

Effect: Cross-site scripting — even when refilling the user's own form. Fix: Escape with htmlspecialchars().

Processing the Form and Showing a Page in the Same Response

Effect: Refreshing the page submits the form again. Fix: Redirect after a successful POST (Post/Redirect/Get).

Expecting JSON in $_POST

Effect: $_POST is empty for JSON requests. Fix: Read php://input and json_decode() it.


Best Practices

  • Use POST for actions that change data and for sensitive or large inputs.
  • Check the request method explicitly, then validate every field on the server.
  • Read fields with defaults and type checks; never assume a field is a string.
  • Refill forms with escaped user input after validation errors.
  • Redirect after successful submissions (PRG), and protect forms with CSRF tokens. Official reference: https://www.php.net/manual/en/reserved.variables.post.php

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else User-defined Functions Slicing Strings Function Arguments PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators AJAX PHP AJAX Database AJAX XML AJAX Live Search AJAX Poll AJAX with JSON Security Introduction Password Hashing SQL Injection Prevention XSS Prevention Garbage Collection Project: Simple Blog PHP What is OOP PHP Classes/Objects The $this Keyword Access Array Items Update Array Items Add Array Items Remove Array Items Sorting Arrays Multidimensional Arrays Array Destructuring Array Spread Operator Search in Arrays Merge Arrays PHP Abstract Classes PHP Interfaces Polymorphism PHP Traits PHP Static Methods PHP Static Properties Late Static Binding Magic Methods Object Cloning MySQL Database MySQL Connect MySQL Create DB MySQL Create Table MySQL Insert Data MySQL Get Last ID MySQL Insert Multiple MySQL Prepared MySQL Select Data MySQL Where Debugging PHP Project: Image Upload Gallery $_SESSION $_ENV PHP RegEx RegEx Patterns Heredoc and Nowdoc RegEx Modifiers preg_match preg_replace preg_split PHP RegEx Functions Nullable Types Mixed and Never Types Nullsafe Operator PHP Attributes PHP Fibers Readonly Classes Property Hooks (PHP 8.4) Asymmetric Visibility (PHP 8.4) Reflection API SPL Data Structures Composer PHP Form Handling PHP XML Parsers PDO Transactions PDO Error Handling PHP XML Parsers PHP SimpleXML Parser PHP SimpleXML - Get PHP XML Expat Parser PHP DOM Parser PHP XMLReader PHP XMLWriter AJAX Intro PHP Class Constants Encryption with Sodium Recursive Functions Variable Functions Anonymous Functions Closures Arrow Functions First-class Callable Syntax PHP Arrays Indexed Arrays Associative Arrays Create Arrays Variables Scope Static Variables Variable Variables PHP Echo / Print PHP printf / sprintf PHP Data Types PHP Booleans PHP NULL PHP Strings String Functions array_map / array_filter / array_reduce Array Functions What Are Superglobals? $GLOBALS What Is `$_SERVER` ? $_REQUEST $_POST $_GET $_FILES $_COOKIE
Ask about this post
AI Ask about this post

Ask questions about $_POST and get answers drawn from it.

Signed-in readers only.