How to provide canonical URL with Django HttpResponseRedirect?
Asked Answered
E

5

9

This question is very similar to one I just asked href: Can I get Google search results to use/display the final redirect url?, but now the question is specific to Django.

My site has webpage urls that use the following format:

www.mysite.com/id/pretty_title

The front page links to these pages, but the href actually contains some parameters:

 www.mysite.com/id/?some_ugly_parameters_to_let_me_know_what_search_it_is_from

This then redirects to

www.mysite.com/id/pretty_title

which shows the page.

My issue is that Google's search results show the link to the page as the ugly url instead of the pretty redirected one.

What I have learned is that I need to provide a canonical link. But how can I do this when the ugly url page never really exists, at least not as one that I have written?

What happens server side is that the view of the ugly url does a redirect:

return HttpResponseRedirect(pretty_url)
Egerton answered 29/3, 2014 at 8:24 Comment(0)
S
8

You can just put it as part of the HTML returned from the Django template, in the <head> section. Do you have a base.html in your Django? You can setup a {% block %} as a placeholder for the canonical URL and then set that value in each individual page that {% extends base.html %}

base.html

<html>
<head>
  <link rel="canonical" href="{% block canonical_url %}{% endblock %}">
</head>
...
Seasickness answered 29/3, 2014 at 8:27 Comment(2)
This html page is displayed by pretty_url. Or does it also 'count' as being displayed by the ugly_url since it redirects to this?Egerton
In your Django there should be only one view that returns that unique content, all you have to do is get the canonical tag into the HTML of that one view. Wether the content from that view is accessible through one or another URL is not important.Seasickness
R
11

I think this is the correct built template tag that you're looking for. {{ request.build_absolute_uri }}

Reins answered 30/9, 2015 at 13:20 Comment(2)
For some reason, build_absolute_uri gives me the http scheme even when the whole website is on https. I'm on Django 1.6 (fairly old, but it's what we've got at the moment).Belize
@Belize <link rel="canonical" href="https://{{ request.get_host }}{{ request.get_full_path }}">Pimbley
S
8

You can just put it as part of the HTML returned from the Django template, in the <head> section. Do you have a base.html in your Django? You can setup a {% block %} as a placeholder for the canonical URL and then set that value in each individual page that {% extends base.html %}

base.html

<html>
<head>
  <link rel="canonical" href="{% block canonical_url %}{% endblock %}">
</head>
...
Seasickness answered 29/3, 2014 at 8:27 Comment(2)
This html page is displayed by pretty_url. Or does it also 'count' as being displayed by the ugly_url since it redirects to this?Egerton
In your Django there should be only one view that returns that unique content, all you have to do is get the canonical tag into the HTML of that one view. Wether the content from that view is accessible through one or another URL is not important.Seasickness
D
3

A lot of these proposed solutions have issues if (1) you want your www subdomain to be the canonical one and (2) there are URL params in the request path.

I would actually propose to hard code it in the base template and append request.path.

<link rel="canonical" href="https://www.example.com{{ request.path }}">

If you do end up wanting to use build_absolute_uri, I would do it as follows in your view (or you could create a template function):

canonical_url = request.build_absolute_uri(request.path)

Calling build_absolute_uri() without an argument will call request.get_full_path() and append that to your domain. If a user finds your site via https://www.example.com/?param=123, your canonical URL will include that param.

Deductive answered 14/5, 2020 at 6:1 Comment(0)
B
0

I agree with @getup8 that the proposed solutions will cause issues. If the URL that was used to access the page is fetched programmatically and that URL is not the canonical one, a non-canonical URL will be declared as canonical. That's not what you want.

What I do instead is have Django fetch the URL of the view, like so:

<link rel="canonical" href="https://www.example.com{% url 'view_name' %}">

Thus ignoring whatever path the user actually used, and always using the standard, base view URL as the canonical one. Yes, this means naming the view in each page, but is still better than hardcoding the URLs.

Blankly answered 3/3 at 20:9 Comment(0)
W
0

Create a python file containing the processing function (anywhere in the project)

in this case, it is the path <project_root>/misc/context.py

from django.http import HttpRequest

def canonical_url(request: HttpRequest):
    return {
        'canonical_url': request.build_absolute_uri(
            request.path
        ).lower(),
    }

we will use the keys of the returned dictionary as variables in the template

Now we must connect the handler to the project

In the main project settings file

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'misc.context.canonical_url'
# -------------- ^^^^^^^^^ add path to function from project root ^^^^^^ -------------
            ],
        },
    },
]
<link rel="canonical" href="{{ canonical_url }}">
Whodunit answered 6/8 at 20:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.