Permitir que o usuário envie arquivos pelo site parece simples, mas é uma das áreas mais delicadas em segurança.
Se você não cuidar direito, alguém pode tentar enviar:
- Arquivos enormes que lotam seu servidor
- Scripts maliciosos disfarçados de imagem
- Extensões falsas só pra explorar alguma falha do PHP/servidor
Neste guia você vai aprender a fazer upload de imagens de forma segura, usando:
$_FILESde forma correta- Validação de tamanho
- Validação de tipo MIME (e não só extensão)
- Renomeação do arquivo para evitar conflitos
- Pasta organizada para uploads
Objetivo: no final você terá um formulário + script PHP que aceita somente imagens válidas, com limite de tamanho e nome seguro.
1. Planejando o upload: o que vamos controlar
Antes de escrever qualquer linha de código, defina algumas regras:
- Quais tipos de arquivo vou permitir?
Ex.: apenasjpg,jpeg,pngewebp. - Qual será o tamanho máximo?
Ex.: no máximo 2 MB por imagem. - Onde esses arquivos vão ficar?
Ex.: uma pastauploads/fora da raiz pública, ou pelo menos bem organizada. - Como vou renomear o arquivo?
Nunca confie no nome que o usuário mandou.
Use algo comouniqid()+ extensão.
Com essas regras em mente, vamos começar.
2. Criando a pasta de uploads
No seu projeto, crie uma pasta chamada uploads (pode ser na raiz ou dentro de uma pasta específica).
Exemplo:
/meu-projeto
upload.php
uploads/
No servidor, garanta que a pasta uploads tenha permissão de escrita (por exemplo, 755 ou 775, dependendo da hospedagem).
Se quiser reforçar a segurança, você pode adicionar um .htaccess dentro da pasta uploads impedindo a execução de scripts, por exemplo:
# uploads/.htaccess
php_flag engine off
Options -ExecCGI
Isso ajuda a impedir que algum arquivo
.phpenviado ali seja executado.
3. Formulário HTML para envio da imagem
Crie um arquivo upload.php com o formulário e também com o processamento (vamos fazer tudo em um só para ficar didático).
Primeiro, a estrutura HTML e o formulário:
<?php
// upload.php
?>
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Upload seguro de imagem em PHP</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
form { max-width: 400px; }
label { display: block; margin-bottom: 8px; }
input[type="file"] { margin-bottom: 10px; }
button { padding: 8px 12px; }
.mensagem { margin-top: 15px; padding: 10px; border-radius: 4px; }
.sucesso { background: #dcfce7; color: #166534; }
.erro { background: #fee2e2; color: #b91c1c; }
</style>
</head>
<body>
<h1>Upload seguro de imagem</h1>
<form method="post" enctype="multipart/form-data">
<label for="imagem">Selecione uma imagem (JPG, PNG ou WEBP):</label>
<input type="file" name="imagem" id="imagem" accept=".jpg,.jpeg,.png,.webp" required>
<button type="submit">Enviar</button>
</form>
</body>
</html>
Pontos importantes:
enctype="multipart/form-data"é obrigatório para upload de arquivos.input type="file"comacceptapenas ajuda o usuário, mas não é segurança.- A segurança de verdade será feita no PHP.
Agora vamos adicionar o código PHP para validar e salvar a imagem.
4. Processando o upload com validações
Vamos colocar o código PHP antes do HTML, no mesmo upload.php, para que ele processe o formulário e depois exiba o resultado.
<?php
// upload.php
// Tamanho máximo em bytes (2 MB)
$tamanhoMaximo = 2 * 1024 * 1024;
// Extensões permitidas
$extensoesPermitidas = ['jpg', 'jpeg', 'png', 'webp'];
// Tipos MIME permitidos
$tiposMimePermitidos = ['image/jpeg', 'image/png', 'image/webp'];
$mensagem = '';
$classeMensagem = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!isset($_FILES['imagem']) || $_FILES['imagem']['error'] !== UPLOAD_ERR_OK) {
$mensagem = 'Erro no upload da imagem. Tente novamente.';
$classeMensagem = 'erro';
} else {
$arquivo = $_FILES['imagem'];
$nomeOriginal = $arquivo['name'];
$tipoMimeEnviado = $arquivo['type']; // não confiável sozinho
$nomeTemporario = $arquivo['tmp_name'];
$tamanhoArquivo = $arquivo['size'];
// 1) Verificar tamanho
if ($tamanhoArquivo > $tamanhoMaximo) {
$mensagem = 'Arquivo muito grande. O limite é de 2 MB.';
$classeMensagem = 'erro';
} else {
// 2) Verificar extensão pela string
$extensao = strtolower(pathinfo($nomeOriginal, PATHINFO_EXTENSION));
if (!in_array($extensao, $extensoesPermitidas)) {
$mensagem = 'Tipo de arquivo não permitido. Envie apenas JPG, PNG ou WEBP.';
$classeMensagem = 'erro';
} else {
// 3) Verificar tipo MIME real usando finfo
$infoArquivo = finfo_open(FILEINFO_MIME_TYPE);
$tipoMimeReal = finfo_file($infoArquivo, $nomeTemporario);
finfo_close($infoArquivo);
if (!in_array($tipoMimeReal, $tiposMimePermitidos)) {
$mensagem = 'O arquivo enviado não parece ser uma imagem válida.';
$classeMensagem = 'erro';
} else {
// 4) Gerar nome seguro e único
$nomeSeguro = uniqid('img_', true) . '.' . $extensao;
// 5) Definir pasta de destino
$diretorioUploads = __DIR__ . '/uploads';
if (!is_dir($diretorioUploads)) {
mkdir($diretorioUploads, 0755, true);
}
$caminhoDestino = $diretorioUploads . '/' . $nomeSeguro;
// 6) Mover o arquivo
if (move_uploaded_file($nomeTemporario, $caminhoDestino)) {
$mensagem = 'Upload realizado com sucesso!';
$classeMensagem = 'sucesso';
// Caminho relativo para exibir a imagem no HTML
$urlImagem = 'uploads/' . $nomeSeguro;
} else {
$mensagem = 'Falha ao mover o arquivo enviado.';
$classeMensagem = 'erro';
}
}
}
}
}
}
?>
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Upload seguro de imagem em PHP</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
form { max-width: 400px; }
label { display: block; margin-bottom: 8px; }
input[type="file"] { margin-bottom: 10px; }
button { padding: 8px 12px; }
.mensagem { margin-top: 15px; padding: 10px; border-radius: 4px; }
.sucesso { background: #dcfce7; color: #166534; }
.erro { background: #fee2e2; color: #b91c1c; }
img.preview { margin-top: 15px; max-width: 300px; display: block; }
</style>
</head>
<body>
<h1>Upload seguro de imagem</h1>
<form method="post" enctype="multipart/form-data">
<label for="imagem">Selecione uma imagem (JPG, PNG ou WEBP):</label>
<input type="file" name="imagem" id="imagem" accept=".jpg,.jpeg,.png,.webp" required>
<button type="submit">Enviar</button>
</form>
<?php if (!empty($mensagem)): ?>
<div class="mensagem <?= htmlspecialchars($classeMensagem) ?>">
<?= htmlspecialchars($mensagem) ?>
</div>
<?php if (isset($urlImagem) && $classeMensagem === 'sucesso'): ?>
<img src="<?= htmlspecialchars($urlImagem) ?>" alt="Imagem enviada" class="preview">
<?php endif; ?>
<?php endif; ?>
</body>
</html>
5. O que esse código está fazendo de importante
- Verifica se houve erro no upload
Usa$_FILES['imagem']['error']para garantir que o arquivo chegou sem problemas. - Valida o tamanho
Compara$_FILES['imagem']['size']com o limite de 2 MB. - Confere a extensão
Usapathinfo()para pegar a extensão em letras minúsculas e compara com uma lista permitida. - Valida o tipo MIME real
Usa a funçãofinfo_file()para checar o tipo real do arquivo e não apenas confiar na extensão. - Gera um nome seguro
Usauniqid('img_', true)para gerar um nome único, evitando sobrescrever arquivos e sem acentos/espaços. - Cria a pasta se não existir
mkdir($diretorioUploads, 0755, true)garante que a pasta exista. - Move o arquivo de forma segura
Usamove_uploaded_file(), que é a forma correta de mover o arquivo recebido via upload. - Não exibe detalhes sensíveis de erro
Para o usuário, mensagens simples. Para log mais detalhado, você pode registrar os erros em arquivo.
6. Cuidados extras (boa prática)
Alguns cuidados adicionais que valem muito a pena:
- Limitar upload apenas para usuários logados, se for uma área administrativa.
- Colocar a pasta de uploads fora da raiz pública e servir os arquivos usando um script PHP (com autenticação).
- Habilitar no servidor alguma proteção para impedir a execução de scripts em pasta de upload (
.htaccess, diretivas do Nginx etc.). - Monitorar o tamanho total ocupado pela pasta
uploads(principalmente em hospedagens mais simples).
7. Conclusão
Com esse modelo de upload, você:
- Aceita apenas imagens reais, verificando extensão e tipo MIME;
- Tem limite de tamanho configurado;
- Evita sobrescrever arquivos com nomes iguais;
- Organiza tudo em uma pasta específica;
- Deixa o caminho pronto para integrar com cadastro de produtos, usuários, perfis etc.
Esse tipo de conteúdo é ótimo pro seu blog porque:
- Resolve uma dúvida muito comum;
- Dá código pronto;
- É fácil de você gravar vídeo/short explicando o mesmo passo a passo.