Índice de contenido
- 1. El problema de devolver la página directamente tras un POST
- 2. ✅ La solución: el patrón PRG (Post/Redirect/Get)
- ¿Qué es una redirección HTTP y por qué importa en Django?
- Qué significa redirigir una vista en Django
- Cuándo usar 301 y cuándo 302: guía rápida para SEO
- Métodos para hacer redirecciones en Django
- Usar redirect() en vistas (el método más directo)
- HttpResponseRedirect y HttpResponsePermanentRedirect
- Redirecciones con reverse() y parámetros dinámicos
- Redirecciones a URLs externas o hacia otros dominios
- redirect() con parámetros posicionales
- Casos prácticos que uso en mis proyectos
- Probar y verificar tus redirecciones
- Cómo testear redirecciones con el Client() de Django
- Usar assertRedirects() en tests automatizados
- Comprobar tus redirecciones con curl o herramientas SEO
- Errores frecuentes y cómo solucionarlos
- NoReverseMatch y namespaces mal definidos
- Redirecciones en login/logout que no funcionan
- Parámetros perdidos o rutas incorrectas
- Preguntas frecuentes sobre redirecciones en Django
- Conclusión
Cuando desarrollamos cualquier proyecto web, Django no es la excepción: además de tener rutas con nombre para organizar nuestras URLs, necesitamos manejar redirecciones. Las redirecciones son una de esas herramientas que usamos casi sin pensarlo, pero que tienen un papel clave tanto en la experiencia del usuario como en el SEO técnico.
Usar bien las redirecciones es lo que marca la diferencia entre una app ordenada y un caos de enlaces rotos.
Las redirecciones en Django son el mecanismo para enviar al usuario de una URL a otra, ya sea dentro de nuestra aplicación o hacia un dominio externo. En términos técnicos, una redirección no es más que una respuesta HTTP de tipo 3xx que le indica al navegador a qué URL debe ir a continuación. Un ejemplo clásico: cuando cambiamos el slug de un post sin querer perder el posicionamiento de la URL original, implementamos una redirección 301 para que Google transfiera el valor SEO hacia el nuevo enlace.
1. El problema de devolver la página directamente tras un POST
Si una vista POST —por ejemplo, la de guardar o actualizar un registro— responde devolviendo directamente el template con render(), ocurre lo siguiente:
- El navegador se queda en un estado
POST. - Si el usuario recarga la página o usa el botón "Atrás", el navegador intentará reenviar la misma petición
POST, lo que puede duplicar el envío, la actualización o incluso la eliminación del registro.
Este comportamiento no solo es un problema de UX, también puede corromper datos si no se maneja con cuidado.
2. ✅ La solución: el patrón PRG (Post/Redirect/Get)
Para evitar que la petición POST se reenvíe involuntariamente, usamos el patrón PRG (Post/Redirect/Get), un estándar muy extendido en el desarrollo web:
- Post: La vista recibe la petición
POSTy procesa la lógica de negocio. - Redirect: Después de guardar o eliminar el registro, la vista devuelve una respuesta de redirección en lugar de renderizar un template.
- Get: El navegador realiza una nueva petición
GETa la URL indicada por la redirección, generalmente el listado o la página de detalle del registro.
¿Qué es una redirección HTTP y por qué importa en Django?
Una redirección HTTP es simplemente una instrucción que le dice al navegador: “no te quedes aquí, ve a otra URL”.
En Django, este comportamiento se implementa con funciones y clases específicas que devuelven una respuesta HTTP de tipo 3xx.
Qué significa redirigir una vista en Django
Cuando una vista procesa un formulario o una acción concreta, muchas veces no tiene un template asociado, porque su única responsabilidad es ejecutar una operación y enviar al usuario a otro lugar. En mis proyectos, por ejemplo, cuando un usuario crea un registro, lo envío directamente a la página de detalle o al listado general, según la lógica de negocio que corresponda.
Códigos de estado más usados: 301, 302 y 307
301(Permanente): indica que la URL ha cambiado definitivamente. Es el código ideal para SEO y para cuando modificas elslugde una página.302(Temporal): se usa cuando la redirección es momentánea, por ejemplo, al redirigir tras enviar un formulario o mientras pruebas una vista.307(Temporal estricto): es una versión más estricta del302; garantiza que el método original (POSToGET) se preserve en la redirección.
Cuándo usar 301 y cuándo 302: guía rápida para SEO
Cuando cambié el slug de un post en mi blog, usé una redirección 301 para no perder tráfico ni backlinks. Los motores de búsqueda interpretan el 301 como una transferencia definitiva del valor SEO de la URL original hacia la nueva.
En cambio, para redirecciones internas tras enviar formularios, siempre uso 302, ya que son transitorias y no deben transferir señales SEO.
Métodos para hacer redirecciones en Django
Las redirecciones son el flujo habitual cuando una vista no tiene un template asociado. Por ejemplo, cuando procesamos la creación de un registro, el resultado esperado es:
- Si los datos son correctos: hacemos una redirección al listado, o al formulario de edición del registro recién creado.
- Si la validación falla: redirigimos de vuelta al formulario de creación mostrando los errores correspondientes.
Para los siguientes ejemplos, partimos de estas rutas definidas en urls.py:
app_name = "gestion"
urlpatterns = [
path('', views.index, name="index"),
path('detail/<int:pk>', views.show, name="show"),
path('create', views.create, name="create"),
path('update/<int:pk>', views.update, name="update"),
path('delete/<int:pk>', views.delete, name="delete"),
]Django ofrece varias formas de redirigir, dependiendo del contexto y del nivel de control que necesitemos.
Usar redirect() en vistas (el método más directo)
La función redirect(), disponible en django.shortcuts, es el método más cómodo y el que uso en la mayoría de mis proyectos. Acepta el nombre de una ruta con namespace, una URL relativa o una URL absoluta:
from django.shortcuts import redirect
return redirect('gestion:index')Un ejemplo más completo dentro de una vista de creación:
from django.shortcuts import redirect
def create(request):
form = ProductForm(request.POST or None)
if form.is_valid():
product = Product()
product.title = form.cleaned_data['title']
# ... resto de los campos ...
product.save()
return redirect('gestion:update', pk=product.id)
return redirect('gestion:create')El patrón es sencillo:
- Si todo sale bien, redirijo al formulario de edición del registro recién guardado.
- Si algo falla en la validación, vuelvo a la vista de creación para que el usuario corrija los errores.
HttpResponseRedirect y HttpResponsePermanentRedirect
Cuando necesitas un control más explícito sobre el código de estado HTTP, puedes usar directamente las clases HttpResponseRedirect (que devuelve un 302) y HttpResponsePermanentRedirect (que devuelve un 301):
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
# Redirección temporal (302)
return HttpResponseRedirect('/productos/')
# Redirección permanente (301) — útil para cambios de slug y SEO
return HttpResponsePermanentRedirect('/nuevo-producto/')Son útiles cuando quieres dejar explícito en el código qué tipo de redirección estás aplicando, sin depender del comportamiento implícito de redirect().
Redirecciones con reverse() y parámetros dinámicos
Django permite construir la URL a partir del nombre de la ruta y sus parámetros usando reverse(). Esto es especialmente útil cuando trabajas con rutas que tienen parámetros dinámicos como pk o slug:
from django.urls import reverse
return redirect(reverse('gestion:show', kwargs={'pk': 15}))Es una práctica más segura que hardcodear URLs, sobre todo si en algún momento cambias la estructura de tus rutas en urls.py: con reverse(), solo tendrías que actualizar el nombre de la ruta en un único lugar.
Redirecciones a URLs externas o hacia otros dominios
Si necesitas redirigir fuera de tu aplicación, simplemente pasa la URL completa como string:
return redirect('https://www.google.com')Ten cuidado con las redirecciones abiertas (open redirects): si la URL de destino proviene de la petición del usuario (por ejemplo, un parámetro ?next=), valídala siempre antes de redirigir para evitar vulnerabilidades de seguridad.
redirect() con parámetros posicionales
También puedes pasar parámetros directamente a redirect() sin necesidad de usar reverse():
return redirect('gestion:update', pk=15)Django construirá la URL automáticamente a partir del nombre de la ruta y el valor del parámetro indicado.
Casos prácticos que uso en mis proyectos
- Redirigir tras crear o actualizar un registro
- En mi flujo habitual, después de guardar un producto, redirijo al detalle o al listado.
Esto mejora la UX y evita que el usuario recargue el formulario accidentalmente, duplicando la operación.
- En mi flujo habitual, después de guardar un producto, redirijo al detalle o al listado.
- Manejo de formularios: validar, guardar y redirigir correctamente
- Si los datos no son válidos, siempre redirijo al formulario original mostrando los errores.
Es un pequeño gesto que evita confusión al usuario y mantiene la app fluida y predecible.
- Si los datos no son válidos, siempre redirijo al formulario original mostrando los errores.
- Cambiar el
slugde un post y redirigir con301para no perder tráfico- Una vez, al actualizar los títulos de varios artículos, los
slugs antiguos quedaron obsoletos. Implementé una redirección301enurls.pyhacia las nuevas rutas. El resultado fue claro: mantuve el tráfico orgánico y no perdí posicionamiento.
- Una vez, al actualizar los títulos de varios artículos, los
- Cómo evitar bucles infinitos y errores comunes
- Un error típico es redirigir a la misma vista que genera la redirección, creando un bucle infinito que el navegador termina cortando con un error
ERR_TOO_MANY_REDIRECTS. - Para evitarlo, verifica siempre las condiciones antes de devolver la respuesta de redirección:
if request.path != reverse('home'): return redirect('home')
- Un error típico es redirigir a la misma vista que genera la redirección, creando un bucle infinito que el navegador termina cortando con un error
Probar y verificar tus redirecciones
Cómo testear redirecciones con el Client() de Django
Puedes automatizar pruebas para asegurarte de que tus vistas redirigen correctamente, sin necesidad de abrir el navegador:
from django.test import Client
client = Client()
response = client.post('/crear/', {'title': 'Nuevo'})
assert response.status_code == 302Usar assertRedirects() en tests automatizados
Más elegante y expresivo aún: el método assertRedirects() verifica tanto el código de estado como la URL de destino en una sola línea:
self.assertRedirects(response, '/listado/')Comprobar tus redirecciones con curl o herramientas SEO
Desde la terminal, puedes inspeccionar los encabezados HTTP de cualquier URL con:
$ curl -I https://tusitio.com/old-url/Así verificas rápidamente si el código devuelto es 301 o 302 y hacia dónde apunta la cabecera Location.
También puedes revisar tus redirecciones en Google Search Console o herramientas como Screaming Frog, que permiten rastrear toda la cadena de redirecciones de tu sitio.
Errores frecuentes y cómo solucionarlos
NoReverseMatch y namespaces mal definidos
Este error ocurre cuando el nombre de la URL no coincide con el registrado en urls.py. Revisa que tengas bien definidos tanto el app_name como los path():
app_name = 'gestion'
path('detalle/<int:pk>', views.show, name='show')Llamar a redirect('gestion:show', pk=1) con el namespace y el nombre correctos solucionará el error.
Redirecciones en login/logout que no funcionan
Si usas vistas genéricas como LoginView, define el parámetro next o success_url correctamente para controlar el destino tras el login:
from django.contrib.auth.views import LoginView
from django.urls import reverse_lazy
class CustomLoginView(LoginView):
success_url = reverse_lazy('dashboard')Parámetros perdidos o rutas incorrectas
Asegúrate de pasar todos los args o kwargs que la ruta requiere; si falta alguno, Django no sabrá cómo construir la URL y lanzará un NoReverseMatch.
Preguntas frecuentes sobre redirecciones en Django
- ¿Cuál es la diferencia entre
redirect()yHttpResponseRedirect?redirect()es un atajo de alto nivel que acepta nombres de rutas, URLs relativas y absolutas, y decide internamente el tipo de respuesta.HttpResponseRedirect, en cambio, siempre devuelve un302y espera una URL explícita.
- ¿Puedo redirigir con parámetros dinámicos?
- Sí, usando
kwargsdirectamente enredirect(), o bien combinando conreverse()para construir la URL con argumentos dinámicos de forma segura.
- Sí, usando
- ¿Qué pasa con el SEO si cambio el
slugde una URL?- Si no implementas una redirección
301, perderás el tráfico orgánico y los backlinks asociados a esa URL. En mi caso, aplicar el301correctamente fue la diferencia entre mantener las posiciones en Google o desaparecer.
- Si no implementas una redirección
- ¿Cómo pruebo mis redirecciones antes de subir a producción?
- Con
curl -I <url>desde la terminal puedes ver el código de estado y la cabeceraLocation. En Google Search Console también puedes detectar redirecciones mal configuradas antes de que afecten a tu posicionamiento.
- Con
Conclusión
Las redirecciones en Django no son solo una herramienta técnica: son una aliada del SEO y de la experiencia de usuario cuando se usan correctamente.
En mis proyectos, aplicar bien los códigos 301 y 302 me ha permitido mantener el tráfico orgánico, evitar errores de duplicación de envíos y tener flujos más claros y predecibles.
Si planificas tus rutas, validas tus formularios y pruebas tus redirecciones antes de producción, te ahorrarás muchos dolores de cabeza. El patrón PRG es sencillo, pero marca la diferencia en apps que crecen.
El siguiente paso es continuar con nuestro CRUD y aprender a eliminar registros en Django.