Se está a ver “Erro 29” no Total Commander ou “Connection Timed Out” no FileZilla, o universo está a enviar-lhe uma mensagem. Pare de usar FTP.
Em 2010, o FTP era o padrão. Em 2026, arrastar e largar ficheiros para um servidor de produção é imprudente. Leva a:
- Downtime: O que acontece se a sua internet falhar enquanto carrega o
functions.php? - Riscos de segurança: O FTP envia palavras-passe em texto simples (a menos que use FTPS).
- Sem histórico: Quem mudou esse ficheiro? Quando? Porquê?
Editar ficheiros PHP diretamente em servidores de produção através de FTP cria vulnerabilidades permanentes e riscos de quebra do serviço. Um pipeline moderno de deployment assente em chaves criptográficas SSH, controlo de versões com Git e publicações atómicas garante consistência, segurança e capacidade de reversão imediata.
Por que razão o FTP é inseguro para WordPress
O protocolo FTP (File Transfer Protocol) transmite credenciais de autenticação e o conteúdo integral dos ficheiros em texto claro. Qualquer interveniente na mesma rede local, num router comprometido ou num nó de alojamento partilhado pode intercetar palavras-passe e ficheiros. Além disso, o FTP não possui registo histórico de alterações, integridade transacional nem mecanismos para coordenar o trabalho entre vários programadores.
O SFTP e o FTPS resolveram a encriptação do tráfego de rede, mas não alteraram as falhas inerentes ao fluxo de trabalho manual. O programador continua a escolher ficheiros manualmente, arriscando esquecimentos e sobrescrevendo código em execução sem rede de segurança. Se a ligação à internet falhar a meio do envio de um ficheiro essencial do tema ou de um plugin, o site apresenta um ecrã branco e falhas críticas aos visitantes.
A arquitetura profissional em 2026 assenta em três pilares fundamentais: SSH para acesso remoto encriptado e autenticação por chaves, Git para controlo de versões e colaboração auditável, e pipelines automatizados para testes, compilação de assets e lançamentos sem tempo de inatividade.
Como configurar SFTP e chaves SSH no WordPress
A autenticação SSH baseada em palavras-passe é vulnerável a ataques sistemáticos de força bruta. O acesso por chaves criptográficas elimina este risco por completo, dado que o servidor apenas concede acesso a clientes em posse da chave privada correspondente.
Gerar um par de chaves ED25519
As chaves ED25519 são mais compactas, mais rápidas e mais seguras do que as chaves RSA tradicionais. Gere um par de chaves através do terminal:
ssh-keygen -t ed25519 -C "[email protected]"Este comando cria dois ficheiros: ~/.ssh/id_ed25519 (a chave privada confidencial, que nunca deve ser partilhada) e ~/.ssh/id_ed25519.pub (a chave pública que é instalada no servidor).
Copiar a chave pública para o servidor
Utilize o utilitário ssh-copy-id para adicionar a chave pública ao ficheiro ~/.ssh/authorized_keys do servidor:
ssh-copy-id [email protected]Após este passo, as ligações SSH ao servidor decorrem automaticamente sem pedido de palavra-passe.
Configurar o ssh-agent e o ficheiro de configuração SSH
Para evitar introduzir a frase de segurança da chave em cada ligação, inicie o ssh-agent:
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519Crie ou edite o ficheiro ~/.ssh/config para definir atalhos de ligação aos ambientes:
Host wppoland-prod
HostName 198.51.100.42
User deploy
IdentityFile ~/.ssh/id_ed25519
Port 22
Host wppoland-staging
HostName 198.51.100.43
User deploy
IdentityFile ~/.ssh/id_ed25519
Port 22Desta forma, o comando ssh wppoland-prod estabelece a ligação de imediato, sem necessidade de memorizar endereços IP ou digitar palavras-passe. Cada elemento da equipa técnica deve possuir o seu próprio par de chaves, sendo expressamente desaconselhada a partilha de chaves privadas.
Como estruturar um repositório Git para WordPress
Um erro frequente ao abandonar o FTP consiste em colocar todo o diretório de instalação do WordPress no Git, incluindo o núcleo do CMS, ficheiros de multimédia e cópias de segurança da base de dados. Um projeto profissional deve rastrear apenas o código personalizado desenvolvido pela equipa.
Estrutura modular do repositório
O repositório armazena exclusivamente temas personalizados, plugins desenvolvidos à medida e mu-plugins. O núcleo do WordPress e os plugins de terceiros são geridos através do Composer ou instalados externamente:
wordpress-project/
├── .gitignore
├── composer.json
├── wp-content/
│ ├── themes/
│ │ └── wppoland-theme/ # versionado
│ ├── plugins/
│ │ └── wppoland-custom/ # versionado
│ └── mu-plugins/ # versionado
└── wp-config.php # NÃO versionadoRegras de exclusão no ficheiro gitignore
O ficheiro .gitignore deve excluir os ficheiros do núcleo do WordPress, carregamentos de multimédia dos utilizadores e configurações sensíveis:
/wp-admin/
/wp-includes/
/wp-content/uploads/
/wp-content/upgrade/
wp-config.php
.env
*.log
node_modules/
vendor/Excluir wp-content/uploads/ previne que o repositório acumule gigabytes de ficheiros multimédia, os quais devem residir em armazenamento de objetos como Amazon S3 ou Cloudflare R2 ou ser sincronizados separadamente.
Gestão de dependências com Composer
Com o Composer e o repositório WPackagist, os plugins e temas de terceiros podem ser declarados como dependências versionadas com versões bem definidas:
{
"require": {
"wpackagist-plugin/advanced-custom-fields": "^6.0",
"wpackagist-plugin/wordpress-seo": "^22.0",
"wpackagist-plugin/wp-super-cache": "^1.9"
},
"repositories": [
{
"type": "composer",
"url": "https://wpackagist.org"
}
]
}A execução de composer install --no-dev --optimize-autoloader no servidor ou no pipeline de integração contínua assegura que todos os ambientes executam exatamente as mesmas versões de dependências.
Como fazer deploy de WordPress com rsync sobre SSH
O Git assegura o histórico do código-fonte, enquanto o utilitário rsync é a ferramenta mais eficaz para transferir ficheiros de compilação para o servidor de produção. O rsync calcula somas de controlo e envia apenas os blocos que sofreram alterações.
Comparativo entre rsync, SCP e FTP
O SCP copia os ficheiros na totalidade em cada execução, mesmo que apenas uma linha tenha mudado. O FTP não oferece sincronização diferencial nem validação automática de somas de controlo. O rsync compara tamanhos e datas, calcula diferenças binárias e transmite unicamente os dados alterados através de um túnel SSH seguro.
Comando rsync com execução de teste previa
Execute sempre uma simulação com --dry-run antes de sincronizar alterações para produção:
rsync -avz --dry-run --delete \
--exclude='.git' \
--exclude='wp-config.php' \
--exclude='wp-content/uploads' \
--exclude='.env' \
--exclude='node_modules' \
./dist/ user@server:/var/www/html/Após verificar a lista de ficheiros afetados, retire a opção --dry-run para executar a transferência efetiva:
rsync -avz --delete \
--exclude='.git' \
--exclude='wp-config.php' \
--exclude='wp-content/uploads' \
--exclude='.env' \
--exclude='node_modules' \
./dist/ user@server:/var/www/html/A opção --delete remove do servidor ficheiros que deixaram de existir no pacote local compilado, prevenindo que código antigo ou ficheiros descontinuados permaneçam em produção. As regras de exclusão salvaguardam ficheiros de multimédia e ficheiros de configuração de ambiente.
Pipelines de CI/CD para WordPress com GitHub Actions
Os fluxos de trabalho do GitHub Actions permitem automatizar testes, verificação de padrões de código com PHP_CodeSniffer e publicação direta em produção a partir de um push na ramificação principal.
name: Deploy WordPress
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Obter código
uses: actions/checkout@v4
- name: Configurar PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
tools: composer
- name: Instalar dependências
run: composer install --no-dev --optimize-autoloader
- name: Verificar padrões PHP
run: vendor/bin/phpcs --standard=WordPress wp-content/themes/wppoland-theme/
- name: Compilar assets de frontend
run: |
npm ci
npm run build
- name: Publicar via rsync
uses: burnett01/[email protected]
with:
switches: -avz --delete --exclude='.git' --exclude='wp-config.php' --exclude='wp-content/uploads'
path: ./
remote_path: /var/www/html/
remote_host: ${{ secrets.SSH_HOST }}
remote_user: ${{ secrets.SSH_USER }}
remote_key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Limpar cache
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: wp cache flush --path=/var/www/htmlOs dados de autenticação são guardados de forma segura nas variáveis secretas do repositório, não ficando quaisquer credenciais expostas no código.
Comandos WP-CLI essenciais para gestão sobre SSH
A ferramenta de linha de comandos WP-CLI permite administrar todos os aspetos do WordPress através do terminal via SSH, sem recorrer ao painel gráfico de administração no navegador.
Atualizações de núcleo, plugins e base de dados
# Atualizar núcleo e aplicar migrações de base de dados
wp core update
wp core update-db
# Instalação e gestão de plugins
wp plugin install wordpress-seo --activate
wp plugin update --all
wp plugin deactivate wordfence
# Otimização e reparação de tabelas MySQL
wp db optimize
wp db repair
# Regenerar chaves de segurança (salts) no wp-config.php
wp config shuffle-salts
# Modo de manutenção para operações de manutenção
wp maintenance-mode activate
wp maintenance-mode deactivateSubstituição precisa de URLs em dados serializados
Ao migrar ambientes ou alterar domínios, a base de dados do WordPress contém estruturas serializadas em PHP nas tabelas wp_options e wp_postmeta. Uma substituição direta por instruções SQL corrompe o comprimento das strings registadas, quebrando widgets e opções de temas. O WP-CLI recalcula o comprimento dos dados de forma automática:
wp search-replace 'https://staging.wppoland.com' 'https://wppoland.com' --all-tables --preciseExecução remota do WP-CLI através de aliases SSH
No ficheiro local ~/.wp-cli/config.yml, pode registar aliases para operar servidores remotos sem necessidade de iniciar sessões interativas de shell:
@prod:
ssh: [email protected]/var/www/html
@staging:
ssh: [email protected]/var/www/htmlA partir da sua máquina local, os comandos podem ser executados diretamente:
wp @prod plugin list
wp @prod cache flush
wp @prod cron event run --due-nowConfiguração de ambientes com phpdotenv e wp-config
Guardar credenciais de base de dados diretamente no ficheiro wp-config.php impede o seu controlo de versões. Uma boa prática de engenharia separa as definições do código através de variáveis de ambiente e da biblioteca vlucas/phpdotenv.
Definição do ficheiro .env por ambiente
Na raiz do projeto, um ficheiro .env específico de cada máquina guarda os parâmetros locais, ficando fora do controlo de versões do Git:
DB_NAME=wppoland_prod
DB_USER=wp_user
DB_PASSWORD=palavra_passe_muito_segura
DB_HOST=localhost
WP_HOME=https://wppoland.com
WP_SITEURL=https://wppoland.com
WP_ENVIRONMENT_TYPE=productionNo ficheiro wp-config.php, o carregador do Composer disponibiliza as variáveis de forma transparente:
<?php
require_once __DIR__ . '/vendor/autoload.php';
if ( file_exists( __DIR__ . '/.env' ) ) {
$dotenv = DotenvDotenv::createImmutable( __DIR__ );
$dotenv->load();
}
define( 'DB_NAME', $_ENV['DB_NAME'] );
define( 'DB_USER', $_ENV['DB_USER'] );
define( 'DB_PASSWORD', $_ENV['DB_PASSWORD'] );
define( 'DB_HOST', $_ENV['DB_HOST'] ?: 'localhost' );
define( 'WP_HOME', $_ENV['WP_HOME'] );
define( 'WP_SITEURL', $_ENV['WP_SITEURL'] );
define( 'WP_ENVIRONMENT_TYPE', $_ENV['WP_ENVIRONMENT_TYPE'] ?: 'production' );
define( 'WP_DEBUG', $_ENV['WP_ENVIRONMENT_TYPE'] !== 'production' );
define( 'WP_DEBUG_LOG', $_ENV['WP_ENVIRONMENT_TYPE'] !== 'production' );
define( 'WP_DEBUG_DISPLAY', false );Desta forma, o ficheiro wp-config.php permanece rigorosamente igual entre o ambiente local, o ambiente de staging e a produção.
Como funcionam as publicações atómicas no WordPress
Fazer deploy com git pull diretamente na pasta pública ou carregar ficheiros por FTP cria um período em que o site fica num estado inconsistente. Enquanto os ficheiros são descarregados, os visitantes podem carregar novos ficheiros PHP que dependem de funções ausentes nos ficheiros que ainda estão a meio da transferência, gerando erros fatais.
Estrutura de pastas inspirada no Deployer e Capistrano
As publicações atómicas resolvem este problema separando cada versão, os recursos partilhados e o link simbólico ativo:
/var/www/omeusite/
├── current -> /var/www/omeusite/releases/20261012120000/
├── releases/
│ ├── 20261012100000/
│ ├── 20261012110000/
│ └── 20261012120000/
└── shared/
├── .env
└── wp-content/
└── uploads/Troca atómica do link simbólico
- Cada lançamento cria uma nova pasta sob
releases/com o respetivo carimbo temporal. - São criados links simbólicos para as pastas partilhadas em
shared/, tais comouploadse.env. - As dependências são instaladas, os ficheiros compilados e os testes executados.
- Concluído o processo com sucesso, o link simbólico
currenté alternado numa única operação atómica no sistema de ficheiros:
ln -sfn /var/www/omeusite/releases/20261012120000 /var/www/omeusite/current_temp && mv -Tf /var/www/omeusite/current_temp /var/www/omeusite/currentA chamada de sistema rename() no Linux opera de forma atómica. O servidor web Nginx ou Apache comuta para a nova versão em microssegundos. Se a instalação ou a compilação falharem, a pasta antiga continua a servir os utilizadores sem qualquer interrupção.
Reversão imediata em caso de anomalia
Se for detetada uma falha após a publicação, não é necessário repor cópias de segurança morosas. Basta apontar o link simbólico para a versão anterior e limpar a cache:
ln -sfn /var/www/omeusite/releases/20261012110000 /var/www/omeusite/current
wp cache flush
sudo systemctl reload php8.3-fpmAutomação com repositórios bare e hooks post-receive no servidor
Para configurações sem recurso a serviços externos de CI/CD, é possível configurar uma solução push-to-deploy diretamente no servidor Linux com um repositório Git bare e um hook post-receive.
Criar um repositório bare no servidor
mkdir -p /var/repo/omeusite.git
cd /var/repo/omeusite.git
git init --bareCriar o hook post-receive
Crie o ficheiro executável /var/repo/omeusite.git/hooks/post-receive:
#!/bin/bash
TARGET_DIR="/var/www/omeusite/releases/$(date +%Y%m%d%H%M%S)"
SHARED_DIR="/var/www/omeusite/shared"
CURRENT_LINK="/var/www/omeusite/current"
echo "===> A iniciar deployment de WordPress..."
mkdir -p "$TARGET_DIR"
# Extrair código da ramificação principal
git --work-tree="$TARGET_DIR" --git-dir=/var/repo/omeusite.git checkout -f main
# Estabelecer ligações a recursos partilhados
ln -s "$SHARED_DIR/.env" "$TARGET_DIR/.env"
rm -rf "$TARGET_DIR/wp-content/uploads"
ln -s "$SHARED_DIR/wp-content/uploads" "$TARGET_DIR/wp-content/uploads"
# Instalar dependências se existir composer.json
if [ -f "$TARGET_DIR/composer.json" ]; then
cd "$TARGET_DIR" && composer install --no-dev --optimize-autoloader --quiet
fi
# Aplicar atualizações de base de dados do WordPress
wp core update-db --path="$TARGET_DIR" --quiet
# Alternar link simbólico de forma atómica
ln -sfn "$TARGET_DIR" "${CURRENT_LINK}_temp" && mv -Tf "${CURRENT_LINK}_temp" "$CURRENT_LINK"
# Limpar cache de objetos e recarregar PHP-FPM
wp cache flush --path="$CURRENT_LINK" --quiet
sudo systemctl reload php8.3-fpm
echo "===> Deployment concluído com sucesso."Atribua permissões de execução ao hook:
chmod +x /var/repo/omeusite.git/hooks/post-receiveAdicione o servidor como destino remoto na máquina local:
git remote add production deploy@server:/var/repo/omeusite.git
git push production mainA cada push, o Git envia apenas as alterações comprimidas por SSH, e o servidor encarrega-se de gerar o novo lançamento e comutar a versão ativa de forma limpa.
Reforço de segurança do servidor após o deployment
Um processo automatizado de publicação requer igualmente a salvaguarda e o reforço da segurança do servidor onde a aplicação reside.
Desativar o protocolo XML-RPC
O protocolo XML-RPC é frequentemente alvo de exploração para ataques de força bruta e amplificação DDoS. Se não for exigido por aplicações móveis ou pelo Jetpack, deve ser desativado através de um ficheiro em mu-plugins:
add_filter( 'xmlrpc_enabled', '__return_false' );No servidor Nginx, o acesso pode ser bloqueado diretamente:
location = /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
}Permissões rigorosas no sistema de ficheiros
O utilizador do processo do servidor web (www-data) nunca deve ser o proprietário dos ficheiros de código PHP. O utilizador responsável pelo deploy detém os ficheiros, enquanto o servidor web apenas necessita de permissão de leitura para execução do código e permissão de escrita limitada à pasta uploads:
# Permissões padrão para ficheiros e pastas
find /var/www/omeusite/current -type f -exec chmod 644 {} \;
find /var/www/omeusite/current -type d -exec chmod 755 {} \;
# Acesso estritamente restrito para ficheiros de variáveis com segredos
chmod 600 /var/www/omeusite/shared/.env
# Acesso de escrita na pasta de uploads para o servidor web
chown -R deploy:www-data /var/www/omeusite/shared/wp-content/uploads
chmod -R 775 /var/www/omeusite/shared/wp-content/uploadsProteção de acessos SSH com fail2ban
Instale e ative o utilitário fail2ban para banir endereços IP com tentativas sucessivas de autenticação falhada:
sudo apt update && sudo apt install fail2ban -y
sudo systemctl enable --now fail2banConfigure o ficheiro /etc/fail2ban/jail.local com regras mais restritivas para o serviço SSH:
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 1h
findtime = 10mRazões para não executar git pull diretamente na pasta pública
Muitas equipas em transição do FTP optam por executar git pull origin main dentro da pasta de produção. Embora proporcione controlo de versões, este método apresenta riscos graves em produção:
- Estados inconsistentes durante a transferência: Enquanto o Git transfere os ficheiros pela rede, os scripts PHP são atualizados de forma não simultânea. Se um utilizador aceder à página nessa fração de segundo, o motor de execução pode encontrar funções inexistentes e gerar erros HTTP 500.
- Conflitos de fusão originados por ficheiros alterados no servidor: Se ficheiros no servidor forem alterados por plugins ou traduções automáticas, o próximo
git pullfalhará com conflitos de merge, deixando o deployment incompleto. - Exposição da pasta .git ao público: Se o repositório residir diretamente na raiz pública e o servidor web não bloquear ficheiros ocultos, invasores externos podem descarregar o histórico do Git e reconstruir o código-fonte da aplicação.
Verificação e testes automáticos após o deploy
O ciclo de publicação só termina após a confirmação do bom funcionamento das páginas:
- Testes de código de estado HTTP: Com o
curl, valida-se que a página inicial e as páginas de conversão respondem com código HTTP 200. - Auditoria de registos de erro: A inspeção imediata do
debug.loge dos registos do PHP-FPM permite intercetar avisos e incompatibilidades antes que afetem os utilizadores. - Verificação de processos críticos: Em lojas WooCommerce, testa-se a navegação no catálogo e o processo de checkout.
Gestão segura de credenciais e segredos no fluxo Git
A adoção de fluxos modernos implica uma política rigorosa na gestão de credenciais:
- As chaves privadas SSH nunca saem do computador local: Ficam guardadas sob frase de segurança e não são partilhadas.
- As pipelines de integração utilizam chaves com privilégios limitados: Chaves dedicadas de deploy garantem apenas o acesso necessário.
- As credenciais da base de dados residem no ficheiro .env: Ficam fora do histórico de commits. Se uma palavra-passe for confirmada por lapso no Git, apagá-la no commit seguinte não resolve a falha, devendo a credencial ser alterada de imediato no servidor.
Lista de tarefas para migração de FTP para Git e SSH
Fase 1: Infraestrutura inicial
- Criar chaves SSH ED25519 para todos os programadores.
- Desativar a autenticação por palavra-passe no ficheiro
/etc/ssh/sshd_config(PasswordAuthentication no). - Instalar o utilitário WP-CLI no servidor.
- Criar atalhos de ligação no ficheiro
~/.ssh/config.
Fase 2: Controlo de versões
- Iniciar o repositório Git e definir um ficheiro
.gitignorerigoroso. - Gerir plugins e temas através de Composer sempre que exequível.
- Estabelecer a estratégia de ramificações com
mainpara produção e ramos específicos para funcionalidades.
- Iniciar o repositório Git e definir um ficheiro
Fase 3: Publicação automatizada
- Configurar um ambiente de staging idêntico ao ambiente de produção.
- Desenvolver scripts de sincronização com
rsyncou Deployer com filtros de exclusão. - Implementar GitHub Actions ou hooks
post-receiveno servidor.
Fase 4: Reforço e monitorização
- Transferir credenciais para ficheiros
.env. - Ajustar permissões no sistema de ficheiros (644 para ficheiros, 755 para pastas, 600 para variáveis).
- Ativar o
fail2bane desativar o XML-RPC. - Testar a capacidade de reversão em caso de emergência.
- Transferir credenciais para ficheiros
Conclusão técnica sobre o deployment moderno de WordPress
O “Erro 29” é um sinal evidente de que a transferência de ficheiros por FTP está ultrapassada.
- Substitua o FTP por SFTP apenas para tarefas manuais de contingência.
- Utilize chaves SSH ED25519 para autenticação segura e sem recurso a palavras-passe.
- Automatize a publicação através de Git e rsync com exclusões criteriosas de dados dinâmicos.
- Aplique publicações atómicas com links simbólicos para evitar tempos de inatividade e permitir reversões imediatas.
Esta metodologia protege a estabilidade do serviço e simplifica o trabalho diário da equipa de desenvolvimento. Descubra os nossos serviços de desenvolvimento e deploy de WordPress na WPPoland.







