---
title: 🚀 Guía: Subdominios Dinámicos en Rails 8 con Kamal 2 y Cloudflare
author: Emmanuel Serna Sandoval
date: 2026-03-09
url: https://eserna27.com/posts/guia-subdominios-dinamicos-en-rails-8-con-kamal-2-y-cloudflare
category: 💻 Código
---
# 🚀 Guía: Subdominios Dinámicos en Rails 8 con Kamal 2 y Cloudflare

Esta arquitectura permite que cada usuario tenga su propia URL (ej: usuario.tuapp.com) compartiendo la misma base de datos y servidor.

1. Configuración de DNS (El "Embudo")Para que cualquier subdominio llegue a tu servidor, necesitas un comodín (Wildcard).

• En Cloudflare: Crea un registro A con el nombre * apuntando a la IP de tu servidor.
• Estado de Proxy: Activa la nube naranja (Proxied). Esto nos da SSL gratuito sin configurar certificados complejos en el servidor.

2. Compartir la Sesión (Login Universal)Por defecto, Rails trata a cada subdominio como un sitio diferente. Para que el usuario no se desloguee al saltar del dominio principal al subdominio, debemos configurar la cookie.

config/initializers/session_store.rb:

Ruby

# El punto inicial permite que la cookie sea válida para todos los subdominios
domain = Rails.env.production? ? ".tuapp.blog" : ".lvh.me"

Rails.application.config.session_store :cookie_store, 
  key: '_tu_app_session', 
  domain: domain,
  tld_length: 2 # Ajusta según tu dominio (ej: .com = 2, .com.mx = 3)3. La "Aduana" de Rutas (Constraint)Necesitamos una clase que decida si una petición debe ir a la "Landing Page" o al "Blog del Usuario".

app/constraints/subdomain_required.rb:

Ruby

class SubdomainRequired
  def self.matches?(request)
    # Filtramos para no atrapar el dominio principal o subdominios técnicos
    request.subdomain.present? && !["www", "admin", "api"].include?(request.subdomain)
  end
end4. El Mapa de Rutas (routes.rb)El orden es vital: Rails lee de arriba hacia abajo. El bloque del subdominio debe ir primero.

Ruby

Rails.application.routes.draw do
  constraints(SubdomainRequired) do
    root "profiles#show", as: :user_blog
    resources :posts
  end

  # Rutas del dominio principal
  devise_for :users
  root "homes#index"
end5. Configuración de Producción (Rails 8)Rails 8 es muy estricto con la seguridad. Hay que configurar el TLD Length (para dominios como .blog) y el Host Authorization.

config/environments/production.rb:

Ruby

# 1. TLD Length: Segmentos del dominio base menos uno (ej: tuapp.blog = 1)
config.action_dispatch.tld_length = 1

# 2. Host Authorization: Permitir subdominios y excluir el Health Check de Kamal
config.hosts << ".tuapp.blog"
config.host_authorization = { exclude: ->(request) { request.path == "/up" } }

# 3. SSL Detrás de Cloudflare (Modo Flexible)
config.assume_ssl = true
config.force_ssl = true6. El Despliegue con Kamal 2Kamal Proxy debe saber que tiene permitido dejar pasar los subdominios hacia el contenedor de la app.

config/deploy.yml:

YAML

proxy:
  # Lista de hosts permitidos
  host: 
    - tuapp.blog
    - "*.tuapp.blog"
  app_port: 3000 # Puerto interno de Rails 8
  ssl: false     # Porque Cloudflare ya cifra el tráfico7. Resumen de comandos útilesAcción

Comando

Actualizar infraestructura

kamal deploy

Solo actualizar el Proxy

kamal proxy reboot

Ver logs del Proxy

kamal proxy logs -f

Ver logs de la App

kamal app logs -f

Consola de producción

kamal app exec -i "bin/rails c"





🛠️ Configuración en Cloudflare (El "Cerebro" de la Red)Cloudflare no solo protege tu sitio, sino que actúa como el director de orquesta para tus subdominios dinámicos.

1. DNS: El Comodín (Wildcard)Para que cualquier-cosa.tuapp.blog funcione sin que tengas que crear registros manuales para cada usuario:

• Tipo: A (o CNAME si usas un alias).
• Nombre (Name): * (el asterisco es el secreto, captura todos los subdominios).
• Contenido (Content): La IP de tu servidor Hetzner.
• Proxy Status: Proxied (Nube Naranja).
• 
  • ¿Por qué? Esto permite que Cloudflare maneje el certificado SSL por ti y oculte la IP real de tu servidor.

“[!TIP] No olvides crear también un registro A para el dominio raíz (@) apuntando a la misma IP.”

2. SSL/TLS: Modo "Flexible"Como en nuestra configuración de Kamal Proxy pusimos ssl: false, necesitamos que Cloudflare se encargue de la "cara pública" (HTTPS) mientras habla con nuestro servidor por un canal privado (HTTP).

• Configuración: Ve a la pestaña SSL/TLS -> Overview.
• Modo: Selecciona Flexible.
• Resultado: El usuario ve https://usuario.tuapp.blog (con candado), pero Cloudflare le pide la información a tu servidor por el puerto 80 (HTTP). Es la forma más rápida de evitar errores de "Handshake" o certificados expirados.

3. Ajustes de Seguridad EsencialesPara garantizar que nadie entre por conexiones no seguras, activa estos dos interruptores en SSL/TLS -> Edge Certificates:

1. Always Use HTTPS: Redirige automáticamente cualquier intento de http:// a https://.
2. Automatic HTTPS Rewrites: Ayuda a solucionar errores de "contenido mixto" si accidentalmente cargas una imagen por HTTP.