Content Index
- 1. The problem with returning the page directly after a POST
- 2. ✅ The solution: the PRG pattern (Post/Redirect/Get)
- What is an HTTP redirect and why does it matter in Django?
- What redirecting a view means in Django
- When to use 301 vs. 302: quick guide for SEO
- Methods for doing redirects in Django
- Using redirect() in views (the most direct method)
- HttpResponseRedirect and HttpResponsePermanentRedirect
- Redirects with reverse() and dynamic parameters
- Redirects to external URLs or other domains
- redirect() with positional parameters
- Practical use cases from my projects
- Testing and verifying your redirects
- How to test redirects using Django's Client()
- Using assertRedirects() in automated tests
- Checking redirects with curl or SEO tools
- Common errors and how to solve them
- NoReverseMatch and misconfigured namespaces
- Login/logout redirects not working
- Missing parameters or incorrect routes
- Frequently asked questions about redirects in Django
- Conclusion
When we develop any web project, Django is no exception: in addition to having named routes to organize our URLs, we need to handle redirects. Redirects are one of those tools we use almost without thinking, but they play a key role in both user experience and technical SEO.
Using redirects properly is what makes the difference between an orderly app and a chaos of broken links.
Redirects in Django are the mechanism to send the user from one URL to another, whether inside our application or to an external domain. In technical terms, a redirect is nothing more than an HTTP response of type 3xx that tells the browser which URL it should go to next. A classic example: when we change the slug of a post without wanting to lose the original URL's ranking, we implement a 301 redirect so that Google transfers the SEO value to the new link.
1. The problem with returning the page directly after a POST
If a POST view —for example, saving or updating a record— responds by directly returning the template with render(), the following happens:
- The browser remains in a
POSTstate. - If the user reloads the page or clicks the "Back" button, the browser will attempt to resend the same
POSTrequest, which can duplicate submission, updating, or even deletion of the record.
This behavior is not only a UX issue, but it can also corrupt data if not handled carefully.
2. ✅ The solution: the PRG pattern (Post/Redirect/Get)
To prevent the POST request from being accidentally resubmitted, we use the PRG (Post/Redirect/Get) pattern, a widely used standard in web development:
- Post: The view receives the
POSTrequest and processes the business logic. - Redirect: After saving or deleting the record, the view returns a redirect response instead of rendering a template.
- Get: The browser makes a new
GETrequest to the URL specified by the redirect, usually the list view or the record detail page.
What is an HTTP redirect and why does it matter in Django?
An HTTP redirect is simply an instruction that tells the browser: “don't stay here, go to another URL.”
In Django, this behavior is implemented with specific functions and classes that return an HTTP response of type 3xx.
What redirecting a view means in Django
When a view processes a form or a specific action, it often doesn't have an associated template, because its only responsibility is to execute an operation and send the user somewhere else. In my projects, for example, when a user creates a record, I send them directly to the detail page or to the general list, depending on the corresponding business logic.
Most common status codes: 301, 302, and 307
301(Permanent): indicates that the URL has changed permanently. It is the ideal code for SEO and when you modify theslugof a page.302(Temporary): used when the redirect is temporary, for example, redirecting after submitting a form or while testing a view.307(Temporary Strict): a stricter version of302; it guarantees that the original method (POSTorGET) is preserved in the redirect.
When to use 301 vs. 302: quick guide for SEO
When I changed the slug of a post on my blog, I used a 301 redirect to avoid losing traffic or backlinks. Search engines interpret 301 as a permanent transfer of SEO value from the original URL to the new one.
On the other hand, for internal redirects after submitting forms, I always use 302, as they are temporary and should not transfer SEO signals.
Methods for doing redirects in Django
Redirects are the usual flow when a view does not have an associated template. For example, when we process record creation, the expected result is:
- If the data is valid: we perform a redirect to the list, or to the edit form for the newly created record.
- If validation fails: we redirect back to the creation form showing the corresponding errors.
For the following examples, we start from these routes defined in 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 offers several ways to redirect, depending on the context and the level of control needed.
Using redirect() in views (the most direct method)
The redirect() function, available in django.shortcuts, is the most convenient method and the one I use in most of my projects. It accepts a namespaced route name, a relative URL, or an absolute URL:
from django.shortcuts import redirect
return redirect('gestion:index')A more complete example inside a creation view:
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']
# ... rest of the fields ...
product.save()
return redirect('gestion:update', pk=product.id)
return redirect('gestion:create')The pattern is simple:
- If everything goes well, I redirect to the edit form for the newly saved record.
- If something fails in validation, I return to the creation view so the user can correct errors.
HttpResponseRedirect and HttpResponsePermanentRedirect
When you need more explicit control over the HTTP status code, you can directly use the HttpResponseRedirect (which returns a 302) and HttpResponsePermanentRedirect (which returns a 301) classes:
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
# Temporary redirect (302)
return HttpResponseRedirect('/productos/')
# Permanent redirect (301) — useful for slug changes and SEO
return HttpResponsePermanentRedirect('/nuevo-producto/')They are useful when you want to make explicit in code which type of redirect you are applying, without relying on the implicit behavior of redirect().
Redirects with reverse() and dynamic parameters
Django allows building the URL from the route name and its parameters using reverse(). This is especially useful when working with routes that have dynamic parameters like pk or slug:
from django.urls import reverse
return redirect(reverse('gestion:show', kwargs={'pk': 15}))It is a safer practice than hardcoding URLs, especially if you ever change your route structure in urls.py: with reverse(), you only need to update the route name in a single place.
Redirects to external URLs or other domains
If you need to redirect outside of your application, simply pass the full URL as a string:
return redirect('https://www.google.com')Be careful with open redirects: if the destination URL originates from user input (e.g., a ?next= parameter), always validate it before redirecting to avoid security vulnerabilities.
redirect() with positional parameters
You can also pass parameters directly to redirect() without needing to use reverse():
return redirect('gestion:update', pk=15)Django will automatically construct the URL using the route name and the provided parameter value.
Practical use cases from my projects
- Redirecting after creating or updating a record
- In my typical workflow, after saving a product, I redirect to detail or list view.
This improves UX and prevents the user from accidentally reloading the form and duplicating the operation.
- In my typical workflow, after saving a product, I redirect to detail or list view.
- Form handling: validate, save, and redirect properly
- If data is invalid, I always redirect back to the original form displaying errors.
It is a simple touch that avoids user confusion and keeps the application fluid and predictable.
- If data is invalid, I always redirect back to the original form displaying errors.
- Changing a post's
slugand redirecting with301to prevent traffic loss- Once, when updating titles for several articles, the old
slugs became obsolete. I implemented a301redirect inurls.pyto the new routes. The result was clear: I maintained organic traffic without losing search engine rankings.
- Once, when updating titles for several articles, the old
- How to avoid infinite loops and common errors
- A common mistake is redirecting to the same view that triggers the redirect, causing an infinite loop that the browser cuts off with an
ERR_TOO_MANY_REDIRECTSerror. - To prevent it, always verify conditions before returning a redirect response:
if request.path != reverse('home'): return redirect('home')
- A common mistake is redirecting to the same view that triggers the redirect, causing an infinite loop that the browser cuts off with an
Testing and verifying your redirects
How to test redirects using Django's Client()
You can automate tests to ensure your views redirect correctly, without needing to open a browser:
from django.test import Client
client = Client()
response = client.post('/crear/', {'title': 'Nuevo'})
assert response.status_code == 302Using assertRedirects() in automated tests
Even cleaner and more expressive: the assertRedirects() method checks both status code and target URL in a single line:
self.assertRedirects(response, '/listado/')Checking redirects with curl or SEO tools
From your terminal, you can inspect HTTP headers for any URL with:
$ curl -I https://tusitio.com/old-url/This lets you quickly verify whether the returned status code is 301 or 302 and where the Location header points.
You can also review your redirects in Google Search Console or tools like Screaming Frog to crawl your site's full redirect chains.
Common errors and how to solve them
NoReverseMatch and misconfigured namespaces
This error occurs when the URL name doesn't match what is defined in urls.py. Make sure both app_name and path() are configured properly:
app_name = 'gestion'
path('detalle/<int:pk>', views.show, name='show')Calling redirect('gestion:show', pk=1) with the correct namespace and name will fix the error.
Login/logout redirects not working
If you use generic views like LoginView, define the next parameter or success_url correctly to handle destination after login:
from django.contrib.auth.views import LoginView
from django.urls import reverse_lazy
class CustomLoginView(LoginView):
success_url = reverse_lazy('dashboard')Missing parameters or incorrect routes
Make sure to pass all required args or kwargs for the route; if any are missing, Django won't know how to build the URL and will throw a NoReverseMatch.
Frequently asked questions about redirects in Django
- What is the difference between
redirect()andHttpResponseRedirect?redirect()is a high-level shortcut that accepts route names, relative URLs, and absolute URLs, internally deciding the response type.HttpResponseRedirect, on the other hand, always returns a302and expects an explicit URL string.
- Can I redirect with dynamic parameters?
- Yes, using
kwargsdirectly inredirect(), or combining it withreverse()to safely construct URLs with dynamic arguments.
- Yes, using
- What happens to SEO if I change a URL's
slug?- If you don't implement a
301redirect, you will lose organic traffic and backlinks associated with that URL. In my case, setting up the301correctly was the difference between maintaining rankings on Google or disappearing.
- If you don't implement a
- How do I test redirects before deploying to production?
- Using
curl -I <url>in the terminal allows you to view status codes and theLocationheader. Google Search Console can also help detect misconfigured redirects before they impact rankings.
- Using
Conclusion
Redirects in Django are not just a technical tool: they are a key ally for SEO and user experience when used properly.
In my projects, properly applying 301 and 302 codes has helped maintain organic traffic, prevent duplicate submission errors, and create clearer, more predictable application flows.
Planning your routes, validating forms, and testing redirects prior to production will save you many headaches. The PRG pattern is simple, but it makes a world of difference as applications scale.
The next step is to continue with our CRUD and learn how to delete records in Django.