Most often, when you're using Django's url tag, you want relative links, not absolute links.

Examples of relative links:

  • /user/103
  • /user/103/edit

Examples of absolute links:

  • http://example.com/user/103
  • https://example.com/user/103/edit

With relative links, you preserve the domain same and protocol (HTTP or HTTPS) of the current user session, with zero work on your part. This is the default behavior of Django's {% url %} tag. Note: confusingly, they refer to "absolute" urls incorrectly in their documentation.

However, in some cases you need to use absolute URLs. For example, in html emails. When the users is in their email client, they don't have the context of what domain/protocol the link is for.

In the past, I would typically change the code of those templates to look something like this:

 <!-- you could also pull the protocol/domain from a setting or from Django's sites metadata -->
 Please <a href="http://example.com{% url user user.id %}">click here<a>.

But what if you're using the same template for website content and email content? You could just output absolute links in both cases. You could put logic into the templates to only output the domain if a certain view scope variable is set. You could also just create separate templates for email/site content. None of these solutions is ideal.

Instead, you could use Django's set_script_prefix. I can't find it in the official documentation, but this handy little method sets a prefix for the current thread context, which will be used on all subsequent url resolutions.

In my case, I just added the following one liner to my mail sending routine before the email content template is rendered.

   set_script_prefix(settings.SITE_URL) # ie, http://example.com