Ir para o conteúdo
Ajuda
Servidor VPS (Cloud)
Servidor VPS (Cloud)

Como hospedar vários sites na mesma VPS com Nginx

Use este guia com IA

Escolha o que você quer e abra no seu assistente preferido.

1 O que a IA deve fazer?

2 Abrir no assistente

Para agentes
Ver .mdVer .json
Compartilhar

Neste artigo

Resposta rápida: para rodar vários sites na mesma VPS, crie um server block do Nginx por domínio em sites-available, ative com um link em sites-enabled e adicione um bloco default_server que recusa domínios desconhecidos. Dê a cada site PHP um usuário Linux e um pool do PHP-FPM próprios, emita um certificado por domínio com o Certbot e aponte o DNS de todos para o mesmo IP. Uma VPS de 4 GB atende um conjunto pequeno de sites estáticos ou WordPress leves.

Quando faz sentido juntar sites em uma VPS

Agências, freelancers e desenvolvedores costumam reunir projetos pequenos em uma VPS para reduzir custo. Um servidor com dez sites de pouco tráfego custa bem menos que dez hospedagens separadas, e você administra um sistema só.

O preço dessa economia é que os sites dividem CPU, memória e disco. Um site com pico ou sob ataque deixa os vizinhos lentos, e uma configuração quebrada no Nginx pode derrubar todos. Quando um site vira o principal do negócio, mova-o para uma VPS própria antes de um incidente.

Como o Nginx escolhe qual site abrir

Todos os domínios chegam ao mesmo IP. O Nginx separa as requisições pelo nome enviado no cabeçalho Host e, no HTTPS, pelo nome enviado no início da conexão TLS (SNI). Ele compara esse nome com o server_name de cada bloco, nesta prioridade:

PrioridadeTipo de server_nameExemplo
1Nome exatoloja.com.br
2Curinga no início (o mais longo vence)*.loja.com.br
3Curinga no fim (o mais longo vence)loja.*
4Expressão regular (vale a primeira na ordem)~^(www\.)?loja\.com\.br$
5Nenhum casou: servidor padrão da portaO bloco com default_server, ou o primeiro carregado

A última linha causa surpresas. Sem um default_server explícito, qualquer domínio apontado para o seu IP, ou o próprio IP digitado no navegador, abre o primeiro site carregado. Isso expõe o site de um cliente em um endereço que não é dele e confunde o Google. A correção está na seção de SSL e segurança.

Estrutura: sites-available e sites-enabled

No Ubuntu e no Debian, cada site fica em um arquivo próprio em /etc/nginx/sites-available/, e o Nginx só carrega o que estiver linkado em /etc/nginx/sites-enabled/. Nomeie cada arquivo com o domínio:

Terminal
# ativar um site
sudo ln -s /etc/nginx/sites-available/site1.com.br /etc/nginx/sites-enabled/
# desativar o site de exemplo que vem com o pacote
sudo rm /etc/nginx/sites-enabled/default
# testar antes de recarregar, sempre
sudo nginx -t && sudo systemctl reload nginx

Rode nginx -t sempre antes de recarregar. Um erro de sintaxe em um site impede o Nginx de aceitar a configuração nova de todos, e um reinício da VPS com esse erro deixa todos fora do ar. Se ainda não tem o Nginx, veja como instalar o Nginx.

Server blocks para site PHP e site estático

Site WordPress ou outra aplicação PHP, em /etc/nginx/sites-available/site1.com.br, já usando o pool próprio da próxima seção:

Terminal
server {
    listen 80;
    server_name site1.com.br www.site1.com.br;
    root /var/www/site1.com.br/public;
    index index.php index.html;

    access_log /var/log/nginx/site1.com.br.access.log;
    error_log  /var/log/nginx/site1.com.br.error.log;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm-site1.sock;
    }

    location ~ /\.(?!well-known) {
        deny all;
    }
}

Site estático, em /etc/nginx/sites-available/site2.com.br:

Terminal
server {
    listen 80;
    server_name site2.com.br www.site2.com.br;
    root /var/www/site2.com.br/public;
    index index.html;

    access_log /var/log/nginx/site2.com.br.access.log;
    error_log  /var/log/nginx/site2.com.br.error.log;

    location / {
        try_files $uri $uri/ =404;
    }
}

Logs separados mostram de onde vem cada erro e cada pico, e a rotação padrão do Ubuntu já cuida deles. Para uma aplicação Node, Python ou container, troque o bloco do PHP por um proxy_pass, como no guia de Next.js na VPS. O exemplo usa PHP 8.3, padrão do Ubuntu 24.04; ajuste o número se a sua versão for outra.

Isolamento: um usuário e um pool do PHP por site

Na instalação padrão, todo o PHP roda como o mesmo usuário em um único pool. Um plugin vulnerável em um site consegue ler e alterar os arquivos de todos. O isolamento básico é dar a cada site um usuário Linux sem login e um pool do PHP-FPM rodando com esse usuário.

Usuário e permissões

Terminal
sudo useradd --system --user-group --home-dir /var/www/site1.com.br --shell /usr/sbin/nologin site1
sudo mkdir -p /var/www/site1.com.br/public
sudo chown -R site1:www-data /var/www/site1.com.br
sudo find /var/www/site1.com.br -type d -exec chmod 2750 {} +
sudo find /var/www/site1.com.br -type f -exec chmod 640 {} +
  • Dono site1: o PHP do site lê e grava os próprios arquivos, como uploads e cache.
  • Grupo www-data com leitura: o Nginx lê CSS, imagens e JavaScript, mas não grava nada.
  • Nenhuma permissão para outros: o usuário do site2 não consegue nem listar a pasta do site1.
  • O 2 do 2750: é o bit setgid, que faz arquivos novos herdarem o grupo da pasta. Sem ele, um upload feito pelo PHP não seria servido pelo Nginx.

Um pool do PHP-FPM por site

Crie /etc/php/8.3/fpm/pool.d/site1.conf:

Terminal
[site1]
user = site1
group = site1
listen = /run/php/php8.3-fpm-site1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 30s
pm.max_requests = 500
Terminal
sudo php-fpm8.3 -t
sudo systemctl reload php8.3-fpm
ls -l /run/php/

O modo ondemand é o que torna vários sites viáveis com pouca RAM: sem visitas, o pool não mantém processos; eles nascem na primeira requisição e morrem depois de 30 segundos parados. O pm.max_children limita quanto um site pode crescer, e o pm.max_requests recicla processos para conter vazamentos de memória de plugins. Para clientes que não se conhecem, containers ou VPS separadas isolam ainda mais.

DNS, SSL e segurança

Cada domínio precisa de registros A, no domínio raiz e no www, apontando para o IP da VPS. Com o DNS resolvendo, emita um certificado por site:

Terminal
sudo certbot --nginx -d site1.com.br -d www.site1.com.br
sudo certbot --nginx -d site2.com.br -d www.site2.com.br
sudo certbot certificates
sudo certbot renew --dry-run

Não junte domínios de clientes diferentes em um certificado só: se um cliente sai e o domínio deixa de apontar para a VPS, a renovação falha para todos os sites daquele certificado.

Agora o bloco padrão que barra domínios desconhecidos, em /etc/nginx/sites-available/padrao:

Terminal
server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    ssl_reject_handshake on;
    return 444;
}

O return 444 fecha a conexão sem resposta, e o ssl_reject_handshake (Nginx 1.19.4 ou mais novo) recusa o HTTPS de nomes sem bloco próprio, sem precisar de certificado. Ative com um link em sites-enabled e teste com curl -I http://IP_DA_VPS, que deve falhar sem resposta.

  • Firewall: libere só 80, 443 e o SSH, de preferência em porta alterada.
  • Fail2ban: opcional, contra força bruta no SSH e no login do WordPress.
  • Backup: copie arquivos e bancos de cada site para fora da VPS, como em backup automático com rsync.

Erros comuns com vários sites

SintomaCausa provávelSolução
502 Bad Gateway em um site PHPSocket do server block diferente do pool, ou pool paradoCompare fastcgi_pass com o listen do pool e veja o status do PHP-FPM
403 Forbidden em tudoNginx não atravessa a pasta ou falta o arquivo de índiceConfira dono, grupo e o modo 2750 das pastas
Um domínio abre o site de outroNome faltando no server_name (como o www) ou DNS erradoRevise o server_name e teste com dig
Upload funciona, mas a imagem não apareceArquivo criado sem o grupo www-dataReaplique o grupo e o bit setgid
Certbot falha em um domínioDomínio ainda não aponta para a VPSConfirme o registro A e rode o Certbot só para esse site

Para ver erros de um site em tempo real, use sudo tail -f /var/log/nginx/site1.com.br.error.log. Para testar um site antes de mudar o DNS, force o nome: curl -I -H "Host: site1.com.br" http://IP_DA_VPS.

Limites e qual VPS usar na RoxyCloud

Com um usuário por site, fica fácil ver quanto cada um consome:

Terminal
ps -o user=,rss= -C php-fpm8.3 | awk '{m[$1]+=$2} END {for (u in m) printf "%s %.0f MB\n", u, m[u]/1024}'
free -h

Quando a soma dos sites passar de 70% da RAM de forma constante, suba de plano ou separe as cargas.

Conjunto de sitesPlano de partida
Até 5 WordPress leves ou dezenas de estáticosVPS Xeon 4 GB (3 vCPU, 40 GB NVMe)
Agência com 10 a 15 sites e uma loja pequenaVPS Xeon 8 GB (4 vCPU, 55 GB NVMe)
Sites com muito PHP fora do cacheVPS Ryzen 9 8 GB (3 vCPU, 100 GB NVMe)

Na RoxyCloud, a VPS Xeon é a escolha padrão para vários sites, por entregar mais núcleos pelo mesmo preço. Todas as VPS têm acesso root, IPv4 dedicado, Anti-DDoS incluso e ficam no datacenter próprio em Presidente Prudente (SP). Se você não quer administrar servidor, a hospedagem de sites com cPanel também comporta vários domínios.

Perguntas frequentes

Quantos sites cabem em uma VPS de 4 GB?

Com pools do PHP em modo ondemand e cache de página, de 3 a 5 sites WordPress leves ou dezenas de sites estáticos. Loja WooCommerce ou site com muito tráfego pede mais RAM ou uma VPS própria.

Um site invadido afeta os outros?

Sem isolamento, sim. Com um usuário e um pool do PHP por site e pastas fechadas para os demais, o estrago fica contido. Para clientes que não se conhecem, containers ou VPS separadas isolam ainda mais.

Preciso de um IP por site?

Não. O Nginx escolhe o site pelo cabeçalho Host e, no HTTPS, pelo SNI. Um único IP atende todos os domínios, cada um com o seu certificado.

Por que um domínio desconhecido abre um dos meus sites?

Porque, sem um bloco default_server, o Nginx usa o primeiro bloco carregado. Crie um bloco padrão que responde 444 e rejeita o handshake TLS.

Um certificado para todos os sites ou um por site?

Um por site. Um certificado com todos os domínios falha na renovação se um deles deixar de apontar para a VPS. Separados, cada um renova sozinho.

Precisa de mais ajuda?

Abra um ticket, entre no Discord ou acesse o painel da RoxyCloud.