How to increase PHP upload size limit

Large uploads can fail before the application code ever sees the request, which blocks media libraries, backup restores, theme or plugin installers, and bulk imports that depend on normal multipart uploads. Raising the effective PHP upload size limit lets legitimate requests reach the application instead of being discarded at the runtime boundary.

The upload ceiling is a chain rather than one setting. upload_max_filesize limits each uploaded file, post_max_size limits the full request body, and larger uploads can still need extra memory_limit headroom while the application parses archives, images, or other heavy payloads. When a request exceeds post_max_size, the body can reach the script without the expected $_POST or $_FILES data.

Verify the same runtime that serves the site, not only the CLI binary in PATH. PHP-FPM pool directives, per-directory .user.ini files on CGI/FastCGI, Apache overrides, or smaller request-body limits in Apache, Nginx, a load balancer, or another reverse proxy can still override the base php.ini file.

Steps to increase PHP upload size limit:

  1. Create a temporary diagnostic script under the same site path that handles uploads.
    $ sudo tee /var/www/app.example.test/public/upload-check.php >/dev/null <<'PHP'
    <?php
    header('Content-Type: text/plain');
    
    printf("SAPI=%s\n", PHP_SAPI);
    printf("Loaded php.ini=%s\n", php_ini_loaded_file() ?: 'none');
    printf("upload_max_filesize=%s\n", ini_get('upload_max_filesize'));
    printf("post_max_size=%s\n", ini_get('post_max_size'));
    printf("memory_limit=%s\n", ini_get('memory_limit'));
    printf("max_input_time=%s\n", ini_get('max_input_time'));
    printf("max_file_uploads=%s\n", ini_get('max_file_uploads'));
    
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $file = $_FILES['upload'] ?? null;
        if (!$file) {
            echo "upload_status=missing\n";
        } elseif ($file['error'] !== UPLOAD_ERR_OK) {
            printf("upload_error=%d\n", $file['error']);
        } else {
            printf("upload_error=0\n");
            printf("upload_name=%s\n", $file['name']);
            printf("upload_size=%d\n", $file['size']);
        }
    }
    PHP

    Keep the probe under the same virtual host, document root, or PHP-FPM pool that receives the real uploads so the result reflects the request-side configuration.

    Remove the diagnostic file after testing because it exposes runtime details to anyone who can reach the URL.

  2. Request the diagnostic script before changing the limit.
    $ curl -sS https://app.example.test/upload-check.php
    SAPI=fpm-fcgi
    Loaded php.ini=/etc/php/8.5/fpm/php.ini
    upload_max_filesize=2M
    post_max_size=8M
    memory_limit=128M
    max_input_time=60
    max_file_uploads=20

    If the response shows apache2handler, inspect Apache override points before changing the global php.ini. If it shows fpm-fcgi, inspect the matching PHP-FPM pool and any nearer .user.ini files before assuming the main file is the active layer.

  3. Search PHP-FPM pool files for an active upload-size override when the diagnostic request reports fpm-fcgi.
    $ sudo grep -R "upload_max_filesize" /etc/php/8.5/fpm/pool.d

    No output means the pool files are not currently overriding the per-file upload limit. If a pool file sets upload_max_filesize, post_max_size, memory_limit, max_input_time, or max_file_uploads, change that pool file instead because it wins for requests handled by that pool.

    On many RHEL-family systems, search /etc/php-fpm.d instead.

  4. Back up the file that currently controls the effective upload limit.
    $ sudo cp /etc/php/8.5/fpm/php.ini /etc/php/8.5/fpm/php.ini.bak-$(date +%Y%m%d%H%M%S)

    Back up the discovered pool file, Apache override file, or existing .user.ini instead when an earlier check shows that layer is setting the live value.

    Keep the backup until a larger upload is confirmed through the same site path.

  5. Open the controlling configuration file in a text editor.
    $ sudoedit /etc/php/8.5/fpm/php.ini

    Open the discovered pool file, Apache override file, or .user.ini instead when that layer owns the active value.

  6. Set the per-file and full-request limits high enough for the largest legitimate upload.
    ; php.ini or .user.ini
    upload_max_filesize = 128M
    post_max_size = 160M
    memory_limit = 256M

    post_max_size must stay larger than upload_max_filesize so the full multipart request can reach PHP. Keep memory_limit above the amount of memory the application needs while parsing or processing uploaded data.

    In a PHP-FPM pool file, use the existing pool syntax, such as php_value[upload_max_filesize] = 128M and php_value[post_max_size] = 160M. Use php_admin_value[] only when the pool should prevent lower layers from changing the value.

  7. Increase max_input_time only when large or slow uploads time out while PHP is still receiving the request body.
    max_input_time = 300

    max_input_time covers request parsing time, including file uploads. max_execution_time applies after script execution begins.

  8. Increase max_file_uploads only when one submission legitimately carries many files.
    max_file_uploads = 50

    PHP counts populated upload fields toward this limit, so blank upload inputs do not consume the quota.

  9. Test the PHP-FPM configuration before reloading the runtime.
    $ sudo php-fpm8.5 -t
    [06-Jun-2026 01:31:26] NOTICE: configuration file /etc/php/8.5/fpm/php-fpm.conf test is successful

    Use the binary that matches the runtime serving the site, such as php-fpm or php-fpm8.4. Skip this syntax test when the only change lives in .user.ini or an Apache override.

    Do not reload the runtime until the configuration test succeeds.

  10. Reload the runtime that serves the application so new workers read the updated directives.
    $ sudo systemctl reload php8.5-fpm

    Reload Apache instead when PHP runs as an Apache module, and use the unversioned php-fpm unit on hosts that package it that way. A .user.ini change is picked up after the user_ini.cache_ttl interval instead of a service reload.

  11. Request the diagnostic script again and confirm the higher live values from the web-facing runtime.
    $ curl -sS https://app.example.test/upload-check.php
    SAPI=fpm-fcgi
    Loaded php.ini=/etc/php/8.5/fpm/php.ini
    upload_max_filesize=128M
    post_max_size=160M
    memory_limit=256M
    max_input_time=300
    max_file_uploads=50

    If smaller values still appear, check for a later PHP-FPM pool override, a nearer .user.ini file, an Apache php_value or php_admin_value override, or a smaller request-body limit in Apache, Nginx, a load balancer, or another reverse proxy.

  12. Create a temporary file that is larger than the old limit but smaller than the new limit.
    $ truncate -s 32M /tmp/upload-smoke.bin

    Use a test size that proves the new limit without stressing the application, disk, or network path.

  13. Post the temporary file through the diagnostic script and confirm upload_error=0.
    $ curl -sS -F upload=@/tmp/upload-smoke.bin https://app.example.test/upload-check.php
    SAPI=fpm-fcgi
    Loaded php.ini=/etc/php/8.5/fpm/php.ini
    upload_max_filesize=128M
    post_max_size=160M
    memory_limit=256M
    max_input_time=300
    max_file_uploads=50
    upload_error=0
    upload_name=upload-smoke.bin
    upload_size=33554432

    If the response is an HTTP 413 before PHP output appears, raise the request-body limit in Nginx, Apache, a load balancer, or another reverse proxy. If upload_status=missing or upload_error=1 appears, recheck post_max_size and upload_max_filesize in the active PHP layer.

  14. Remove the temporary upload file.
    $ rm /tmp/upload-smoke.bin
  15. Remove the temporary diagnostic script.
    $ sudo rm /var/www/app.example.test/public/upload-check.php

    Leaving the file reachable keeps the active SAPI, loaded configuration path, and runtime limits exposed to anyone who can request the URL.