csrf_token shown in browser headers: Misunderstood "internal urls"?

I work on a webpage where language and darkmode are set by a POST form. I just realized that the csrf_token gets leaked in the browser headers for the POST request:

csrftoken=…; sessionid=

Each of them goes to a subdomain (/i18n/ and /darkmode/). Trying to find out why they leak I checked the docu: Is a subdomain considered an external URL? If yes: What would I do in that case? Without the token I get a 403 because the CSRF token is missing. If no: Could it be that the token gets leaked because I don’t use render or RequestContext in the views?

Here is one of the template-parts where I perform the post-request:

<form class= "language_dropdown" action="{% url "set_language" %}" method="post" id="language_selector_form">
        {% csrf_token %}

Here is the corresponding view I inherited from my collegue:

def set_language(request):
    Redirect to a given URL while setting the chosen language in the session
    (if enabled) and in a cookie. The URL and the language code need to be
    specified in the request parameters.

    Since this view changes how the user will see the rest of the site, it must
    only be accessed as a POST request. If called as a GET request, it will
    redirect to the page in the request (the 'next' parameter) without changing
    any state.
    next_url = request.POST.get('next', request.GET.get('next'))
    if (
        (next_url or request.accepts('text/html')) and
        not url_has_allowed_host_and_scheme(
        next_url = request.META.get('HTTP_REFERER')
        if not url_has_allowed_host_and_scheme(
            next_url = '/'
    response = http.HttpResponseRedirect(next_url) if next_url else HttpResponse(status=204)
    lang_code = None
    if request.method == 'POST':
        lang_code = request.POST.get(LANGUAGE_QUERY_PARAMETER)
    if request.method == 'GET':
        lang_code = request.GET.get(LANGUAGE_QUERY_PARAMETER)
    if lang_code and check_for_language(lang_code):
        if next_url:
            next_trans = translate_url(next_url, lang_code)
            if next_trans != next_url:
                response = http.HttpResponseRedirect(next_trans)
        if hasattr(request, 'session'):
            # Storing the language in the session is deprecated.
            # (RemovedInDjango40Warning)
            request.session[LANGUAGE_SESSION_KEY] = lang_code

            settings.LANGUAGE_COOKIE_NAME, lang_code,
    return response

What do you mean by “leaked”? Why do you think this is a problem that needs to be addressed?

Also, I’m confused by you calling /i18n/ and /darkmode/ “subdomains”. They are not subdomains, they are directories within the domain you are issuing the request to.

If your normal request is to https://www.example.com/whatever, then a subdomain would look something like https://sub.www.example.com/whatever.

Thank you for your answer, I think I misunderstood csrf itself. The token is only secret up to the moment the browser performs the request? In the docu on How to use CSRF it says that the csrf_token tag should not be used for external urls since it would leak the token. I think I use it on an internal one here, but it gets shown in the headers. So by “leak” the docu here means “send to an external url”, just showing it to the user that performs the post request isn’t considered a problem?

Obviously the user has access to all data being sent to/from his browser. By “secret”, they mean that it’s information shared only between the server and the user.

Yes, by “leak”, they’re saying that if you get a CSRF token from site www.example-1.com, you should not include that token in any POST data being sent to www.example-2.com.

The browser itself will not include the cookie version of the token on any requests to www.example-2.com - that is not something you need to be concerned about.