PT
Iniciar sessão Criar conta

Os erros mais comuns ao implementar o Django em produção e como os evitar

Bad Request (400), admin sem estilos, CSRF verification failed? Aqui estão os 10 erros mais comuns ao implementar o Django em produção, como os reconhecer e corrigir, e ainda uma lista de verificação.

Grzegorz Kalmus ·

A aplicação Django funciona perfeitamente no teu portátil, mas depois de a colocares no servidor, aparece "Bad Request (400)", o painel de administração sem estilos ou uma página em branco com um erro 500. Não é azar, são apenas algumas armadilhas recorrentes em que quase toda a gente cai na primeira implementação. O ambiente de desenvolvimento Django é intencionalmente tolerante: mostra erros, serve ficheiros estáticos por si só e não pergunta sobre o domínio. A produção não perdoa nada disso.

Neste guia, reunimos os 10 erros mais comuns ao implementar Django em produção. Em cada um, descrevemos como se manifesta, de onde vem e como o corrigir. No final, há uma pequena lista de verificação para verificar antes de cada implementação e respostas às perguntas mais frequentes.

Índice

  1. Porque é que o Django funciona localmente, mas não no servidor?
  2. Erro 1: O DEBUG = True em produção é um grande problema?
  3. Erro 2: Onde devo guardar o SECRET_KEY e as palavras-passe?
  4. Erro 3: De onde vem o "Bad Request (400)"?
  5. Erro 4: Porque é que aparece "CSRF verification failed" atrás de um proxy?
  6. Erro 5: Porque é que o painel de administração não tem estilos?
  7. Erro 6: Posso deixar o runserver?
  8. Erro 7: O que acontece quando te esqueces das migrações?
  9. Erro 8: O SQLite é suficiente para a produção?
  10. Erro 9: Porque é que o pip install deita a implantação abaixo?
  11. Erro 10: Como sabes se a aplicação caiu?
  12. Lista de verificação pré-implantação
  13. Como funciona na Saplo?
  14. FAQ

Porque é que o Django funciona localmente, mas não no servidor?

O comando python manage.py runserver inicia um servidor de desenvolvimento que, silenciosamente, faz muitas coisas em nome do programador. Serve ficheiros CSS e JavaScript, exibe uma página de erro legível com um stack trace quando ocorre um erro, aceita pedidos em localhost e não se preocupa com o HTTPS.

Em produção, esse conforto desaparece. A aplicação está atrás de um servidor web ou de um proxy reverso, funciona sob um domínio real, o tráfego chega encriptado e cada elemento que, localmente, “funcionava sozinho”, tem de ser explicitamente configurado. A maioria dos erros descritos abaixo é precisamente a diferença entre esses dois mundos.

Antes de continuar, lembra-te de um comando. O Django tem uma auditoria integrada para as configurações de produção:

python manage.py check --deploy

Isto lista avisos sobre o DEBUG, chave, cookies, HTTPS e cabeçalhos de segurança. Executá-lo antes da primeira implementação resolve metade dos problemas desta lista.

Erro 1: O DEBUG = True em produção é um grande problema?

Sim, é. Com o DEBUG ativado, qualquer erro 500 mostra ao visitante uma página de diagnóstico completa: fragmentos de código, variáveis locais, caminhos no servidor, uma lista de configurações e queries SQL. O Django mascara alguns nomes sensíveis, mas não todos, e a própria estrutura da aplicação é um mapa valioso para um atacante. Além disso, o modo DEBUG memoriza cada query da base de dados, pelo que, após um tempo, a memória do processo aumenta.

Como corrigir: define DEBUG a partir de uma variável de ambiente, com um valor padrão seguro.

import os
DEBUG = os.environ.get("DJANGO_DEBUG", "0") == "1"

Localmente, defines DJANGO_DEBUG=1, e no servidor não defines nada. Lembra-te apenas que, depois de desativares o DEBUG, ALLOWED_HOSTS entra em vigor e o Django deixa de servir ficheiros estáticos. É por isso que os erros 3 e 5 costumam aparecer logo após a correção deste.

Erro 2: Onde guardar a SECRET_KEY e as palavras-passe?

Não no repositório. A SECRET_KEY assina as sessões, os tokens de redefinição de palavra-passe e os cookies CSRF. Quem a conhece, pode falsificar a sessão de um utilizador autenticado. A chave gerada pelo startproject geralmente acaba no primeiro commit e fica no histórico do git para sempre, mesmo depois de ser removida do ficheiro.

O mesmo se aplica à palavra-passe da base de dados, às chaves da API para pagamentos ou correio.

Como corrigir: lê todos os segredos das variáveis de ambiente e deixa apenas os nomes no repositório.

SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]

A ausência da variável causará um erro ao iniciar, e ainda bem: é melhor que a aplicação não inicialize do que inicializar com a chave predefinida. Se a chave já foi parar a um repositório público, gera uma nova. Todos os utilizadores serão desconectados, mas a chave antiga deixará de ser uma ameaça.

Erro 3: De onde vem o "Bad Request (400)"?

Quase sempre de um ALLOWED_HOSTS vazio ou incompleto. Com o DEBUG desativado, o Django rejeita qualquer pedido cujo cabeçalho Host não esteja nesta lista. Esquecimentos típicos incluem a falta da versão com www, a falta de um subdomínio temporário sob o qual a aplicação funcionava antes da conexão do domínio, ou um erro de digitação.

Como corrigir: insere todos os domínios nos quais a aplicação deve responder.

ALLOWED_HOSTS = ["asuaempresa.com", "www.asuaempresa.com"]

Não te safes com a entrada ["*"]. Isso desativa a proteção contra ataques ao cabeçalho Host, como a substituição de um domínio falso em links de redefinição de palavra-passe.

Erro 4: Porque é que "CSRF verification failed" aparece atrás do proxy?

Em produção, o Django raramente recebe tráfego diretamente. À sua frente está um proxy reverso, que aceita HTTPS e encaminha HTTP normal para a aplicação. O Django vê, portanto, um pedido não encriptado, e o navegador envia um formulário do endereço https://. A partir da versão 4.0, o Django compara o cabeçalho de Origem com uma lista de fontes de confiança e, sem ele, rejeita os formulários, incluindo o login no painel de administração.

Uma segunda variante do mesmo problema: ativas SECURE_SSL_REDIRECT = True, e a página entra num ciclo infinito de redirecionamentos, porque o Django continua a pensar que o pedido veio via HTTP.

Como corrigir: diz ao Django qual cabeçalho o proxy usa para transmitir informações HTTPS e adiciona fontes de confiança.

SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
CSRF_TRUSTED_ORIGINS = ["https://asuaempresa.com", "https://www.asuaempresa.com"]
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

A definição SECURE_PROXY_SSL_HEADER é segura apenas se o proxy definir sempre este cabeçalho e não passar valores do cliente.

Erro 5: Porque é que o painel de administração não tem estilos?

Porque quando o DEBUG está desativado, o Django não serve ficheiros estáticos. O painel /admin/ funciona, mas parece uma página dos anos noventa, e os CSS e imagens personalizados devolvem 404. Além disso, há o esquecido collectstatic: os ficheiros estão nas diretorias da aplicação, mas ninguém os reuniu num só lugar.

Como corrigir: define STATIC_ROOT, recolhe os ficheiros a cada implementação e serve-os através de um servidor web ou da biblioteca WhiteNoise.

STATIC_ROOT = BASE_DIR / "staticfiles"
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    # reszta middleware
]
python manage.py collectstatic --noinput

O WhiteNoise só serve ficheiros estáticos do código. Os ficheiros carregados pelos utilizadores (MEDIA_ROOT, por exemplo, fotos de produtos) devem ser servidos separadamente, através de um servidor web ou de um armazenamento de ficheiros externo. Esta é uma segunda desilusão comum, logo após a primeira.

Erro 6: Posso deixar o runserver?

Não. A Documentação Django afirma que o servidor de desenvolvimento não passou por auditorias de segurança nem testes de desempenho. Ele lida com as requisições de forma inadequada para o tráfego, não possui um gerenciamento de processos sensato e, se falhar, ninguém o reiniciará.

Como corrigir: executa a aplicação através de um servidor WSGI, geralmente Gunicorn, sob o controlo de um gestor de processos que o irá reiniciar após uma falha ou reinício da máquina.

gunicorn yourproject.wsgi:application --bind 127.0.0.1:8000 --workers 2

O número de processos de worker depende da memória. Cada processo Gunicorn é uma cópia separada da aplicação na RAM, por isso, num servidor pequeno com 1 GB, é melhor começar com dois e observar o consumo, do que definir oito e ter os processos encerrados pelo sistema.

Erro 7: O que acontece quando te esqueces das migrações?

A nova versão do código faz referência a uma coluna que ainda não existe na base de dados. A página que usa este modelo devolve 500, e os logs mostram no such column ou relation does not exist. Uma variante deste erro é um ficheiro de migração que não foi parar ao repositório porque estava em .gitignore ou ninguém o adicionou.

Como corrigir: trata as migrações como um passo constante em cada implementação, e não como uma ação manual.

python manage.py makemigrations --check --dry-run
python manage.py migrate --noinput

O primeiro comando, executado antes do commit, termina com um erro se existirem alterações nos modelos sem um ficheiro de migração. O segundo é executado no servidor antes do arranque da nova versão. Antes de uma migração que remove colunas ou tabelas, faz uma cópia da base de dados.

Erro 8: O SQLite é suficiente para produção?

Para uma aplicação pequena com poucos utilizadores e poucas gravações, o SQLite pode funcionar durante anos. Os problemas começam com muitas gravações simultâneas (database is locked) e com cópias de segurança: o ficheiro da base de dados copiado durante a gravação pode ficar corrompido.

O verdadeiro erro não é a escolha do SQLite em si, mas sim manter a base de dados num local onde desaparece aquando da implementação. O ficheiro db.sqlite3 num diretório que é sobrescrito a cada implementação ou num contentor sem um volume persistente significa que a próxima implementação começa com uma base de dados vazia.

Como corrigir: se a aplicação for crescer, muda para PostgreSQL e fornece o endereço da base de dados na variável DATABASE_URL. Independentemente do motor da base de dados, certifica-te de que os dados estão num local persistente e que estão cobertos por uma cópia de segurança.

Erro 9: Por que é que o pip install impede a implementação?

Porque o ambiente local é diferente do ambiente do servidor. Cenários comuns: o ficheiro requirements.txt não tem versão, então o servidor transfere uma biblioteca mais recente e incompatível; um pacote instalado manualmente localmente, que não está presente no ficheiro (No module named ...); ou uma versão diferente do Python. Exemplo prático: o Django 6.0 requer Python 3.12 ou mais recente, então num servidor com Python 3.11, o comando pip install django sem restrição de versão falhará ou resultará numa instalação que não arranca.

Como corrigir: fixa as versões e verifica qual a versão do Python que está a ser executada no servidor.

pip freeze > requirements.txt
python --version

Se o servidor tiver Python 3.11, adiciona Django>=5.2,<6 ao ficheiro de dependências. O ramo 5.2 tem suporte de longo prazo, o que o torna uma escolha sensata para produção.

Erro 10: Como sabes se a aplicação falhou?

O pior cenário é aquele em que o cliente te informa sobre a falha. Com o DEBUG desativado, os erros não são visíveis na página, por isso, se não forem para os registos (logs) ou para o e-mail, simplesmente desaparecem.

Como corrigir:

  • configura LOGGING para que os erros sejam enviados para a saída padrão ou para um ficheiro que possas rever após uma falha,
  • configura ADMINS e uma configuração de e-mail correta para que o Django envie um e-mail a cada erro 500,
  • adiciona um endereço de verificação simples, por exemplo, /health/, que retorna {"status": "ok"}, e associa-o ao monitor de disponibilidade.

Um endereço de verificação também será útil para implementações: o sistema de implementação pode verificar se a nova versão está ativa antes de redirecionar o tráfego para ela.

Lista de verificação pré-implementação

Antes de lançares uma nova versão, verifica estes pontos:

Ponto Como verificar
DEBUG desativado DJANGO_DEBUG não está definido no servidor
Segredos fora do repositório git grep SECRET_KEY não mostra valores
Domínios em ALLOWED_HOSTS versão com www e sem, mais subdomínio temporário
HTTPS atrás do proxy SECURE_PROXY_SSL_HEADER e CSRF_TRUSTED_ORIGINS definidos
Ficheiros estáticos collectstatic no processo de implementação, admin tem estilos
Servidor de aplicações Gunicorn em vez de runserver
Migrações makemigrations --check passa, migrate no processo de implementação
Base de dados e cópias de segurança dados em local persistente, cópia de segurança funciona
Dependências versões fixadas, compatíveis com Python no servidor
Logs e monitorização erros vão para os logs, /health/ responde
Auditoria Django python manage.py check --deploy sem avisos

Como funciona na Saplo?

Na Saplo, a maioria desta lista já está configurada antes de escreveres a primeira linha de código. Quando crias uma aplicação Django a partir do catálogo, a receita instala Gunicorn e WhiteNoise, configura STATIC_ROOT, insere o domínio em ALLOWED_HOSTS e CSRF_TRUSTED_ORIGINS, adiciona SECURE_PROXY_SSL_HEADER, executa migrações e collectstatic, e o processo da aplicação funciona sob a supervisão de um gestor de processos que o reinicia após uma falha. O certificado SSL para o domínio está incluído no preço do plano.

Ao implementares o teu próprio repositório, os passos são controlados pelo ficheiro saplo.yaml. As migrações são inseridas como um gancho pré-build, e a recolha de estáticos como um comando de build:

build:
  install: "pip install -r requirements.txt"
  command: "python manage.py collectstatic --noinput"
hooks:
  pre_build: "python manage.py migrate --noinput"

Algumas coisas importantes a saber:

  • Python 3.11. O builder executa o Python 3.11 do sistema, por isso, em requirements.txt, mantém o Django no ramo 5.x (erro 9).
  • Segredos. SECRET_KEY ou a palavra-passe da base de dados são guardados com o comando saplo env set --secret ou no Painel, no separador Variáveis e segredos. O Painel, a API e o assistente de IA mostram apenas asteriscos. A alteração é válida a partir da próxima implementação ou reinício. Detalhes na Documentação de variáveis e segredos.
  • Uma implementação falhada não derruba o site. Se a build ou a verificação de saúde falharem, a nova versão obtém um estado de erro, e a versão anterior continua a funcionar. A verificação de saúde espera por uma resposta até 60 segundos.
  • PostgreSQL é configurado como um contentor no ficheiro docker-compose.yml da aplicação, com a porta exposta apenas a 127.0.0.1 e o endereço na variável DATABASE_URL.
  • Cópias de segurança. Cada Box tem uma cópia de segurança diária encriptada, enviada para outro centro de dados (7 dias para Box S, M e L). A partir do Box L, são adicionados snapshots automáticos para restauração com um clique, e em qualquer plano, um snapshot pode ser feito manualmente antes de uma migração arriscada.
  • Logs podem ser visualizados com o comando saplo logs --follow ou no Painel, nos detalhes da implementação.

A implementação a partir do GitHub após cada git push é descrita nas instruções de Git deploy, e as mensagens de erro típicas foram recolhidas na resolução de problemas.

Qual plano? Para uma pequena aplicação Django, o Box S é suficiente (1 GB RAM, 27 € por ano). Se for necessário acesso SSH ao servidor ou uma base de dados PostgreSQL num contentor separado, um bom começo é o Box M (2 GB RAM, SSH, 65 € por ano). O Box L (6 GB RAM, 139 € por ano) adiciona snapshots automáticos de 7 dias. O preço do primeiro ano é o mesmo que o da renovação, e a migração de uma aplicação existente de outro hosting é feita gratuitamente. Uma comparação completa dos planos está no Preço.

Se estás a começar com Django, lê primeiro o que é Django e para que serve, e as instruções de configuração podem ser encontradas no guia como configurar Django na Saplo.

FAQ

Como posso verificar se a minha aplicação Django está pronta para produção?

Executa python manage.py check --deploy com as configurações de produção. O comando irá listar avisos sobre DEBUG, chave, HTTPS, cookies e cabeçalhos de segurança. Depois, revê a lista de verificação neste artigo, porque a auditoria não verifica migrações, backups ou ficheiros estáticos.

Porque é que o site mostra "Bad Request (400)" depois de desativar o DEBUG?

Porque, a partir desse momento, o Django verifica o ALLOWED_HOSTS e o domínio através do qual estás a aceder não está na lista. Adiciona todas as variantes do endereço, incluindo o www e o subdomínio temporário, e reinicia a aplicação.

O WhiteNoise é suficiente para ficheiros estáticos em produção?

Para a maioria das aplicações de pequena e média dimensão, sim. O WhiteNoise serve ficheiros com compressão e um longo tempo de armazenamento na cache do navegador. No entanto, não suporta ficheiros carregados pelos utilizadores, que devem ser servidos separadamente.

De quanta RAM precisa o Django em produção?

Uma aplicação simples com dois processos Gunicorn cabe em algumas centenas de megabytes, então um servidor com 1 GB de RAM é suficiente para começar. É necessária mais memória com uma base de dados PostgreSQL no mesmo servidor, uma fila de tarefas em segundo plano ou um maior número de processos de trabalho.

Posso migrar uma aplicação Django que já esteja a funcionar para o Saplo?

Sim. A aplicação é implementada a partir de um repositório Git ou via CLI, e a migração de outro alojamento é feita gratuitamente em qualquer plano. Antes da Migração, vale a pena verificar a lista de controlo neste artigo, porque as configurações dependentes do servidor antigo, como Domínios ou caminhos de ficheiros, devem ser ajustadas para o novo.

  • #Segurança
  • #Django
  • #Guia
  • #Python
  • #Implantação