Você editou o php.ini, salvou, recarregou a página — e o valor continua o mesmo. Quase sempre a causa é uma destas duas: você editou o php.ini errado (cada SAPI do PHP tem o seu, e o do terminal não é o do site) ou você não recarregou o PHP-FPM (o arquivo é lido uma única vez, quando o processo sobe). O comando php --ini, que todo tutorial manda rodar, mostra o arquivo do CLI — justamente o que a sua página não usa. Abaixo: por que funciona assim, como descobrir o arquivo certo em trinta segundos, a hierarquia completa que decide o valor final, e por que às vezes nem o arquivo certo resolve.
O mecanismo: uma SAPI, um php.ini
SAPI (Server API) é o modo como o PHP foi acionado. O mesmo PHP instalado na máquina roda em modos diferentes: cli quando você digita php script.php no terminal, fpm-fcgi quando o nginx repassa uma requisição ao PHP-FPM. Cada modo carrega sua própria configuração — o que faz sentido, porque as necessidades são opostas: um script de terminal pode rodar por horas e consumir muita memória; uma página web não deve.
Em instalações por pacote (Debian/Ubuntu), a separação é literal, um diretório por SAPI:
/etc/php/8.3/cli/php.ini ← o que o `php --ini` mostra
/etc/php/8.3/fpm/php.ini ← o que o seu SITE usa
/etc/php/8.3/fpm/conf.d/ ← arquivos extras, carregados depois
/etc/php/8.3/fpm/pool.d/www.conf ← configuração do pool (mais específica ainda)
Em imagens Docker oficiais o layout é outro (um /usr/local/etc/php/php.ini compartilhado + conf.d), e em painéis de hospedagem pode ser outro ainda. Por isso a resposta nunca é decorar caminhos — é perguntar ao próprio site qual arquivo ele carregou.
O segundo pilar do mecanismo: o php.ini é lido quando o processo inicia. O master do PHP-FPM lê a configuração ao subir e os workers nascem já com aqueles valores. Enquanto o master continuar vivo, ele não relê arquivo nenhum — você pode editar e salvar quantas vezes quiser que nada muda. É a mesma lógica de qualquer daemon: configuração nova exige avisar o processo.
Diagnóstico: pergunte ao site, não ao terminal
Este é o passo que economiza a tarde inteira. Crie um arquivo temporário na raiz pública e abra pelo navegador (não pelo terminal):
<?php
phpinfo();
Na página, procure três campos no topo:
Server API— deve dizerFPM/FastCGI. Se disserCommand Line Interface, você rodou pelo terminal e está vendo a configuração errada.Loaded Configuration File— este é o arquivo que importa. É ophp.inique a sua página realmente carregou.Scan this dir for additional .ini files/Additional .ini files parsed— os arquivos doconf.dque são lidos depois e podem sobrescrever o principal.
Compare com o que o terminal diz (php --ini) e a diferença aparece na hora. Se os dois apontam arquivos diferentes, você achou a causa.
Para checar um valor específico sem expor tudo, uma linha basta — e ela mostra o valor efetivo, já com todas as camadas aplicadas:
<?php
echo ini_get('memory_limit'); // o que está valendo AGORA, nesta requisição
# o caminho que parece certo e não é
php --ini
# Loaded Configuration File: /etc/php/8.3/cli/php.ini
sudo nano /etc/php/8.3/cli/php.ini # ← ini do TERMINAL
# altera memory_limit, salva, recarrega a página… nada muda# 1. descubra o ini do FPM pelo phpinfo() aberto no NAVEGADOR
# Loaded Configuration File: /etc/php/8.3/fpm/php.ini
sudo nano /etc/php/8.3/fpm/php.ini # ← ini do SITE
sudo php-fpm8.3 -t # 2. valide a configuração
sudo systemctl reload php8.3-fpm # 3. aplique (graceful)
# confira de novo no phpinfo/ini_getA hierarquia: quem ganha de quem
Mesmo no arquivo certo, o valor pode estar sendo sobrescrito depois. O PHP monta a configuração em camadas, e a última que fala vence:
1. php.ini principal
A base. É o arquivo que o Loaded Configuration File aponta.
; /etc/php/8.3/fpm/php.ini
memory_limit = 128M
2. Arquivos do conf.d
Lidos depois, em ordem alfabética do nome. Por isso arquivos costumam ser prefixados com números (99-overrides.ini vence 10-opcache.ini). Instalar uma extensão joga um arquivo aqui — e ele pode sobrescrever o que você editou no principal.
; /etc/php/8.3/fpm/conf.d/99-app.ini
memory_limit = 256M ; ← agora vale 256M
3. Configuração do pool (FPM)
Mais específica ainda: vale só para aquele pool. Duas formas, com uma diferença crucial.
; /etc/php/8.3/fpm/pool.d/www.conf
php_value[memory_limit] = 512M ; pode ser mudado por ini_set
php_admin_value[memory_limit] = 512M ; NÃO pode ser mudado por ini_set
4. ini_set() em runtime
A camada mais específica: vale só para aquela requisição, a partir daquela linha.
ini_set('memory_limit', '1G'); // só nesta execução
A regra final
Da mais genérica para a mais específica: php.ini → conf.d → pool → ini_set. Quando o valor não é o que você espera, a pergunta certa é "quem falou por último?" — e o ini_get() responde.
echo ini_get('memory_limit'); // o vencedor da disputa
A diferença entre php_value e php_admin_value não é detalhe de estilo — está documentada no próprio arquivo de exemplo que acompanha o FPM: "php_admin_value/php_admin_flag — these directives won't be overwritten by PHP call ini_set". Ou seja, se o administrador definiu php_admin_value[memory_limit], o seu ini_set('memory_limit', ...) no código não tem efeito e você fica procurando bug onde não há. É o mecanismo usado em hospedagem compartilhada para impor limites.
ini_set() não muda tudo
Mesmo sem php_admin_value no caminho, há diretivas que ini_set() simplesmente não consegue alterar — e ele falha em silêncio, devolvendo false sem lançar erro. Cada diretiva tem um "modo" que define onde pode ser definida: PHP_INI_ALL (em qualquer lugar, inclusive runtime), PHP_INI_PERDIR (só em php.ini/pool/.user.ini) e PHP_INI_SYSTEM (só em php.ini/pool). Testado:
| Diretiva | ini_set() |
Por quê |
|---|---|---|
memory_limit |
✅ muda | PHP_INI_ALL |
max_execution_time |
✅ muda | PHP_INI_ALL |
display_errors |
✅ muda | PHP_INI_ALL |
upload_max_filesize |
❌ false | PHP_INI_PERDIR |
post_max_size |
❌ false | PHP_INI_PERDIR |
disable_functions |
❌ false | PHP_INI_SYSTEM |
Faz sentido: o tamanho máximo de upload precisa ser conhecido antes de o PHP começar a receber o corpo da requisição — quando o seu script roda, o upload já foi aceito ou recusado. Por isso ini_set('upload_max_filesize', '64M') nunca funcionou para ninguém. Se ini_set() devolveu false, a diretiva exige arquivo + reload.
O terceiro motivo (e uma distinção importante)
Existe um caso que chega ao Google com a mesma frase — "mudei e não fez efeito" — mas tem outra causa: o OPcache. Vale separar bem as duas, porque confundi-las custa horas:
- O OPcache não guarda valores do
php.ini. Se você mudou uma diretiva e recarregou o FPM, o OPcache não tem nada a ver com isso. - O OPcache guarda o bytecode do seu código PHP. Se você editou um arquivo
.phpe a mudança não aparece, aí sim o suspeito é ele — especialmente comopcache.validate_timestamps = 0, configuração comum em produção por performance, que manda o PHP nunca checar se o arquivo mudou.
Nesse caso, o conserto é o mesmo comando: systemctl reload php8.3-fpm limpa o cache de bytecode, porque os workers novos começam com a memória compartilhada revalidada. É por isso que "recarregar o FPM" resolve tanta coisa aparentemente diferente.
Você aumentou memory_limit para 512M. No terminal, php -r "echo ini_get('memory_limit');" mostra 512M. No site, continua 128M. O que aconteceu?
Ver resposta
Resposta certa: Você editou o php.ini do CLI; o do FPM continua com o valor antigo (e/ou faltou reload)
O terminal e o site usam SAPIs diferentes, cada uma com seu php.ini — o php --ini aponta o do CLI. Alterar lá muda só os scripts de terminal. Abra um phpinfo() pelo navegador, leia o Loaded Configuration File, edite aquele arquivo e rode systemctl reload php8.3-fpm. E descarte o OPcache como suspeito: ele guarda bytecode do seu código, não valores de configuração.
Resumo de decisão
| Sintoma | Causa provável | Conserto |
|---|---|---|
| Muda no terminal, não no site | php.ini do CLI ≠ do FPM |
Achar o do FPM no phpinfo() |
| Arquivo certo e nada muda | Faltou recarregar | php-fpm -t + systemctl reload |
| Valor volta ao antigo sozinho | Sobrescrito no conf.d ou no pool |
Conferir a ordem; ini_get() mostra o vencedor |
ini_set() não surte efeito |
php_admin_value no pool |
Alterar no pool (ou pedir ao admin) |
ini_set() devolve false |
Diretiva PHP_INI_PERDIR/SYSTEM |
Só via arquivo + reload |
| Mudei o código e não mudou | OPcache com validate_timestamps=0 |
systemctl reload php8.3-fpm |
Em uma frase: descubra o arquivo pelo phpinfo() servido na web, edite aquele, valide com -t, recarregue com reload e confirme com ini_get() — nessa ordem, você nunca mais mexe no escuro.
A página do arquivo de configuração explica a ordem de leitura do php.ini, os arquivos adicionais do conf.d e como o PHP decide qual carregar em cada SAPI.
Esta é a referência dos modos PHP_INI_ALL, PHP_INI_PERDIR e PHP_INI_SYSTEM — ela diz, diretiva por diretiva, onde cada uma pode ser definida. É o que explica por que ini_set() funciona para umas e não para outras.
As diretivas do OPcache (validate_timestamps, revalidate_freq, enable_cli) determinam quando o PHP relê seus arquivos. Entendê-las separa o "configuração não aplicou" do "código não atualizou".