Tutorial: Publicar gratis una web estática con Flask + Frozen‑Flask en Netlify
Guía paso a paso, clara y genérica, para convertir una app Flask sencilla en un sitio estático y publicarla gratis en Netlify. Incluye requisitos, estructura, ejemplos de código, build, despliegue y una sección amplia de problemas comunes con sus soluciones.
1) Objetivo
- Renderizar tus páginas Flask a HTML estático (sin servidor Python) y servirlas en Netlify.
- Beneficios: hosting gratuito, CDN global, HTTPS automático y despliegues por Git.
2) Requisitos previos
- Python 3.10+ instalado.
- Git instalado y repositorio en GitHub/GitLab/Bitbucket (Netlify se integra con ellos).
- Línea de comandos (Windows PowerShell, macOS Terminal o Linux Shell).
3) Estructura mínima del proyecto
.
├─ app.py # App Flask con rutas
├─ freeze.py # Script para "congelar" (generar HTML estático)
├─ netlify.toml # Configuración de build en Netlify
├─ requirements.txt # Dependencias
├─ templates/ # Plantillas Jinja2 (*.html)
└─ static/ # CSS, imágenes, JS
4) Dependencias
Archivo requirements.txt
:
Flask>=3.0,<4.0
Frozen-Flask>=0.18
Instalación local (ejecuta línea por línea):
python -m venv venv
# Windows
venv\Scripts\Activate.ps1
# macOS/Linux
source venv/bin/activate
python -m pip install -r requirements.txt
5) Código mínimo
app.py
(app Flask básica):
from flask import Flask, render_template, url_for
app = Flask(__name__)
@app.get("/")
def index():
return render_template("index.html")
@app.get("/about/") # Nota: barra final para sitio estático
def about():
return render_template("about.html")
if __name__ == "__main__":
app.run(debug=True)
templates/index.html
(ejemplo mínimo):
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Inicio · Mi Sitio</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}" />
</head>
<body>
<nav>
<a href="/">Inicio</a>
<a href="/about/">Sobre mí</a>
</nav>
<main>
<h1>Hola Mundo</h1>
<p>Este sitio está generado con Flask y servido como HTML estático.</p>
</main>
</body>
</html>
templates/about.html
(ejemplo mínimo):
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Sobre mí · Mi Sitio</title>
</head>
<body>
<nav>
<a href="/">Inicio</a>
<a href="/about/">Sobre mí</a>
</nav>
<main>
<h1>Sobre mí</h1>
<p>Texto de ejemplo.</p>
</main>
</body>
</html>
static/css/styles.css
(opcional):
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; }
nav a { margin-right: 12px; text-decoration: none; color: #0a66c2; }
6) Congelación (generar HTML estático)
Archivo freeze.py
:
from __future__ import annotations
import shutil
from flask_frozen import Freezer
from app import app
BUILD_DIR = "build"
def main() -> None:
freezer = Freezer(app)
@freezer.register_generator
def static_pages():
# Rutas con barra final para generar /about/index.html
for path in ["/", "/about/"]:
yield path
# Limpia la salida y genera
shutil.rmtree(BUILD_DIR, ignore_errors=True)
freezer.freeze()
if __name__ == "__main__":
main()
Generar el sitio:
python freeze.py
Se creará la carpeta build/
con:
build/index.html
build/about/index.html
build/static/...
(assets referenciados conurl_for('static', ...)
)
7) Configurar Netlify
- Crea el archivo
netlify.toml
en la raíz del repo:
[build]
command = "pip install -r requirements.txt && python freeze.py"
publish = "build"
[build.environment]
PYTHON_VERSION = "3.11"
-
Sube el repo a GitHub/GitLab/Bitbucket.
-
En Netlify:
- New site from Git → selecciona tu repositorio.
- Deja el comando y directorio de publicación según
netlify.toml
. - Deploy.
- Para reconstrucciones limpias, usa “Clear cache and deploy site” en Deploys.
8) Buenas prácticas para sitios estáticos
- Usa rutas con barra final (e.g.,
/about/
) para que el host sirva.../index.html
conContent-Type: text/html
correcto. - Usa
url_for('static', filename='...')
para referenciar CSS/JS/imagenes. - Evita rutas relativas quebradizas; en plantillas, genera enlaces absolutos (
/ruta/
). - No dupliques manualmente assets a
build/
; Frozen-Flask copia lo necesario. - Verifica tu
build/
localmente abriendo los HTML antes de publicar.
9) Problemas comunes y cómo resolverlos
-
“Veo el HTML como texto en el navegador”
- Causa: generaste archivos
about.html
sueltos sin carpetaabout/index.html
o el host no infiereindex.html
. - Solución: usa rutas con barra final y registra esas rutas en
freeze.py
.
- Causa: generaste archivos
-
“404 en subpáginas al hacer clic”
- Causa: enlaces sin barra final o rutas no registradas.
- Solución: añade las rutas a
static_pages()
y usa enlaces como/about/
.
-
“No carga el CSS o imágenes”
- Causa: referencias a archivos con rutas manuales erróneas.
- Solución: usa
{{ url_for('static', filename='css/styles.css') }}
y confirma quebuild/static/...
existe.
-
“El build en Netlify falla”
- Revisa Deploy logs: puede faltar
requirements.txt
, o el comando de build está mal. - Asegura
PYTHON_VERSION
compatible (3.10/3.11) ennetlify.toml
. - Verifica que
freeze.py
no copie manualmente a destinos ya existentes (evitacopytree
abuild/...
).
- Revisa Deploy logs: puede faltar
-
“Los cambios no se reflejan tras deploy”
- Causa: caché de Netlify o navegador.
- Solución: “Clear cache and deploy site”; recarga dura (Ctrl+F5) o abre en incógnito.
-
“En local funciona, en Netlify no”
- Causa: rutas relativas o dependencias faltantes.
- Solución: usa rutas absolutas (con
/
), revisarequirements.txt
y el comando de build.
-
“Quiero SPA o rutas dinámicas”
- Este flujo es para HTML estático renderizado por Flask en build-time. Para SPA usa frameworks JS o funciones serverless.
10) Checklist de publicación
- [ ]
requirements.txt
con Flask y Frozen-Flask. - [ ] Rutas definidas (con barra final cuando aplique).
- [ ]
freeze.py
limpiabuild/
y congela rutas necesarias. - [ ]
build/
generado localmente con HTML + assets. - [ ]
netlify.toml
conpublish = "build"
ycommand
correcto. - [ ] Deploy en Netlify finalizado sin errores.
11) Comandos rápidos
# Preparación
python -m venv venv
venv\Scripts\Activate.ps1 # Windows
# source venv/bin/activate # macOS/Linux
python -m pip install -r requirements.txt
# Generar sitio estático
python freeze.py
# Git
git add .
git commit -m "Publicar sitio estático"
git push
# Netlify: Clear cache and deploy (en el panel web)
Con esto tendrás una web estática generada con Flask + Frozen‑Flask y publicada gratis en Netlify, de forma simple, reproducible y profesional.
Curso de Flask