Servidores multi-IP y SEO

Cientos de sitios en un servidor con varias IP: una IP por sitio, plantillas Nginx y SSL

6 pasos 18 min de lectura 25 vistas 0
Contenido

Un servidor para redes de sitios (site cluster) con uno o varios bloques /24 puede alojar cientos de sitios web, cada uno en su propia dirección IP. Hacer clic cientos de veces en un panel de control es lento y propenso a errores; un pequeño conjunto de plantillas y un script hacen lo mismo en minutos y mantienen todos los sitios configurados de forma idéntica. Esta guía muestra cómo planificar la asignación de dominios a IP, evitar que los sitios aparezcan en la IP equivocada, generar virtual hosts de Nginx a partir de una plantilla, emitir certificados SSL en masa con acme.sh y comprobar que cada dominio responde en su propia IP.

Esta guía da por hecho que todas las IP ya están configuradas en el servidor (consulte configurar un /24 completo en Linux) y que el registro A de cada dominio apunta a la IP que le asigne. Pruebe el script con dos o tres dominios antes de ejecutarlo para cientos.

Paso 1: Planificar la asignación

Mantenga un único archivo de texto plano como fuente de referencia: una línea por sitio con el dominio y su IP. Todo lo demás se genera a partir de él.

mkdir -p /root/cluster
cat > /root/cluster/sites.txt <<'EOF2'
example-a.com 203.0.113.10
example-b.com 203.0.113.11
example-c.net 203.0.113.12
EOF2

# Check that every IP in the list is actually bound on the server
awk '{print $2}' /root/cluster/sites.txt | while read ip; do ip -br addr | grep -qw "$ip" || echo "NOT BOUND: $ip"; done

Paso 2: Evitar que los sitios aparezcan en otras IP

Nginx elige primero la dirección de escucha (IP y puerto) y después compara el nombre de host; una solicitud con un nombre de host desconocido la responde el default server (servidor predeterminado) de esa dirección, que, si no se define, es el primer sitio configurado en ella. En una red de sitios, eso significa que quien abra la IP directamente, o le envíe el nombre de host de otro sitio, verá uno de sus sitios, lo que permite relacionarlos entre sí. Cierre esas solicitudes en dos lugares: un catch-all para las IP que todavía no tienen ningún sitio (abajo) y un default server por IP en la plantilla (paso 3).

cat > /etc/nginx/conf.d/00-default.conf <<'EOF2'
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 444;
}
EOF2

# Hide the Nginx version in headers and error pages
sed -i 's/^\s*#\?\s*server_tokens.*/    server_tokens off;/' /etc/nginx/nginx.conf
grep -q 'server_tokens off' /etc/nginx/nginx.conf || sed -i '/http {/a\    server_tokens off;' /etc/nginx/nginx.conf

Paso 3: Crear la plantilla de virtual host

La plantilla usa marcadores que el script reemplaza para cada sitio. Cada sitio escucha solo en su propia IP, y las solicitudes para cualquier otro nombre de host en esa IP se cierran.

mkdir -p /etc/nginx/sites-cluster /www/sites
cat > /root/cluster/vhost-http.tpl <<'EOF2'
# Unknown hostnames on this IP: close the connection
server {
    listen __IP__:80 default_server;
    server_name _;
    return 444;
}

server {
    listen __IP__:80;
    server_name __DOMAIN__ www.__DOMAIN__;
    root /www/sites/__DOMAIN__/public;
    index index.html index.php;
    access_log /www/sites/__DOMAIN__/logs/access.log;
    error_log  /www/sites/__DOMAIN__/logs/error.log;

    location /.well-known/acme-challenge/ { allow all; }
    location / { try_files $uri $uri/ =404; }
}
EOF2

# Load the generated files from the http block
grep -q 'sites-cluster' /etc/nginx/nginx.conf || sed -i '/http {/a\    include /etc/nginx/sites-cluster/*.conf;' /etc/nginx/nginx.conf

Paso 4: Generar todos los sitios con un solo script

cat > /root/cluster/deploy.sh <<'EOF2'
#!/bin/bash
set -e
while read -r DOMAIN IP; do
  [ -z "$DOMAIN" ] && continue
  mkdir -p /www/sites/$DOMAIN/public /www/sites/$DOMAIN/logs
  [ -f /www/sites/$DOMAIN/public/index.html ] || echo "<h1>$DOMAIN</h1>" > /www/sites/$DOMAIN/public/index.html
  sed -e "s/__DOMAIN__/$DOMAIN/g" -e "s/__IP__/$IP/g" /root/cluster/vhost-http.tpl > /etc/nginx/sites-cluster/$DOMAIN.conf
done < /root/cluster/sites.txt
chown -R www-data:www-data /www/sites 2>/dev/null || chown -R nginx:nginx /www/sites
nginx -t && systemctl reload nginx
EOF2
chmod +x /root/cluster/deploy.sh && /root/cluster/deploy.sh

Suba el contenido de cada sitio a /www/sites/DOMAIN/public. Para añadir sitios más adelante, agregue líneas a sites.txt y vuelva a ejecutar el script; el contenido existente no se sobrescribe.

Paso 5: Emitir certificados SSL en masa

Instale acme.sh y configure Let's Encrypt como CA (consulte SSL gratuito con Certbot o acme.sh). El siguiente bucle emite un certificado para cada dominio a través de su directorio raíz web, lo instala y añade un bloque server HTTPS en la IP propia del sitio.

cat > /root/cluster/ssl.sh <<'EOF2'
#!/bin/bash
mkdir -p /etc/nginx/ssl
while read -r DOMAIN IP; do
  [ -z "$DOMAIN" ] && continue
  [ -f /etc/nginx/ssl/$DOMAIN.crt ] && continue          # already done
  ~/.acme.sh/acme.sh --issue -d $DOMAIN -d www.$DOMAIN -w /www/sites/$DOMAIN/public || { echo "FAILED: $DOMAIN"; continue; }
  ~/.acme.sh/acme.sh --install-cert -d $DOMAIN \
     --key-file /etc/nginx/ssl/$DOMAIN.key --fullchain-file /etc/nginx/ssl/$DOMAIN.crt \
     --reloadcmd "systemctl reload nginx"
  cat >> /etc/nginx/sites-cluster/$DOMAIN.conf <<EOF3
server {
    listen $IP:443 ssl default_server;
    ssl_reject_handshake on;      # unknown hostnames on this IP (Nginx 1.19.4+)
}
server {
    listen $IP:443 ssl;
    http2 on;
    server_name $DOMAIN www.$DOMAIN;
    ssl_certificate     /etc/nginx/ssl/$DOMAIN.crt;
    ssl_certificate_key /etc/nginx/ssl/$DOMAIN.key;
    root /www/sites/$DOMAIN/public;
    index index.html index.php;
    access_log /www/sites/$DOMAIN/logs/access.log;
}
EOF3
  sleep 5
done < /root/cluster/sites.txt
nginx -t && systemctl reload nginx
EOF2
chmod +x /root/cluster/ssl.sh && /root/cluster/ssl.sh

Ejecútelo en lotes de unas pocas decenas de dominios; Let's Encrypt limita cuántos certificados nuevos puede solicitar una cuenta en poco tiempo. Los dominios cuyo DNS todavía no apunte a la IP correcta fallarán; corrija el DNS y vuelva a ejecutar el script, que omite los que ya están hechos. La renovación es automática mediante la tarea cron de acme.sh.

Paso 6: Verificar cada sitio

# Each domain must answer on its own IP with HTTP 200 (or 301 if you redirect to HTTPS)
while read -r DOMAIN IP; do
  code=$(curl -s -o /dev/null -w '%{http_code}' --resolve $DOMAIN:80:$IP http://$DOMAIN/)
  dns=$(dig +short $DOMAIN A | tail -1)
  echo "$DOMAIN $IP http=$code dns=$dns $( [ "$dns" = "$IP" ] && echo OK || echo DNS-MISMATCH)"
done < /root/cluster/sites.txt

# The bare IP must not show any site (connection closed = correct)
curl -sI http://203.0.113.10/ || echo "closed as expected"
curl -skI https://203.0.113.10/ || echo "handshake rejected as expected"

Preguntas frecuentes

¿Puedo hacerlo con aaPanel?

Sí: aaPanel permite vincular cada sitio a una IP en la configuración del sitio, lo que es suficiente para unas pocas decenas de sitios (consulte asignar una IP dedicada a cada sitio web). Para cientos de sitios, las plantillas y los scripts son más rápidos y menos propensos a errores, y puede seguir usando aaPanel para las bases de datos y la supervisión.

¿El tráfico saliente también debe usar la IP propia de cada sitio?

Si sus sitios obtienen contenido o llaman a API externas, las solicitudes salientes salen de forma predeterminada por la IP principal del servidor. Para que usen la IP de cada sitio, consulte la sección sobre la IP de salida en asignar una IP dedicada a cada sitio web.

¿Cómo debo repartir los sitios entre bloques de IP?

En las redes de sitios para SEO, repartir los sitios entre varios bloques /24 (distintas clases C) y mantener diferenciados las plantillas, las cuentas de analítica y los datos de hosting importa más que el número de IP. Consulte servidores para redes de sitios SEO con varias IP de clase C.

¿Sigue sin resolverlo después de seguir estos pasos? Abra un ticket de soporte y el equipo técnico de IMIDC, disponible 24/7, le ayudará. Incluya la IP del servidor, la versión del sistema operativo, el dominio y la IP que fallan y la salida de nginx -t para que podamos localizar el problema más rápido.

¿Fue útil la respuesta?

Tutoriales relacionados