In Django templates, what does autoescaping do to a {{ review.body }} value, and which characters does it convert?
answer
- on by default for the DTL
- five characters, one of them the ampersand
- applied after filters, at output
- values already marked are skipped
- literals and one block tag opt out
basics
~20 sDjango's template engine HTML-escapes every variable's output by default, converting < > ' " and & to entities, after filters run and unless the value is already marked safe, so user text like a product review renders as text, not markup.
solid answer
~40 sWith the default `DjangoTemplates` backend, autoescaping is on: when a `{{ }}` variable is rendered, Django converts `<` to `<`, `>` to `>`, `'` to `'`, `"` to `"` and `&` to `&`. It happens at output time, after any filters, and it is skipped for values already marked safe (`SafeString`, from `mark_safe`, `format_html` or the `|safe` filter). So a review body containing `<script>` shows up as visible text. `{% autoescape off %}...{% endautoescape %}` disables it for a block, and string literals written in the template itself, such as a `default` filter argument, are never escaped because the template author controls them. Autoescaping is HTML escaping only: it protects element content and quoted attributes, not JavaScript or URL contexts.
code
django · 7 lines{# review.body == '<script>steal()</script> Great blender & quiet' #}
<p>{{ review.body }}</p>
{# output: <p><script>steal()</script> Great blender & quiet</p> #}
<p title="{{ review.title }}">{{ review.rating }}/5</p>
{{ review.summary|default:"No summary — yet" }} {# literal: not escaped #}go deeper
Recall that Django escapes every {{ }} variable by default and name the five characters it converts, so user text shows as text.
Explain that escaping happens at output after filters, that safe strings are skipped, and which exceptions exist: autoescape off, literals and safe values.
Show that you know autoescaping is HTML-only and reason about unquoted attributes, href values and script blocks where it does not protect you.
Treat every escape hatch as a reviewable exception and set team rules for quoting attributes and handling non-HTML templates rather than disabling escaping.
## What autoescaping is **Autoescaping** is the Django template language's default behaviour of HTML-escaping the output of every variable. On a product-review page, a reviewer can type anything into the review body, including `<script>` or `<img onerror=...>`. If that text were inserted into the page verbatim, the browser would treat it as markup. Autoescaping turns it into harmless text before it reaches the response. It is enabled by default for the `DjangoTemplates` backend (the engine's `autoescape` option defaults to `True`), so a plain template needs no extra work: ```django <article class="review"> <h3>{{ review.title }}</h3> <p>{{ review.body }}</p> </article> ``` ## The five replacements | Character | Becomes | |---|---| | `<` | `<` | | `>` | `>` | | `'` | `'` | | `"` | `"` | | `&` | `&` | Escaping quotes as well as angle brackets is what lets the same mechanism protect a value placed inside a **quoted** attribute, such as `title="{{ review.title }}"`. ## When it happens 1. The variable is resolved. 2. Any filters in the expression run, in order. 3. At the moment the value is written to the output, Django converts it to a string and escapes it — **unless** the value is already a **safe string**. A safe string is an instance of `SafeString` (a `str` subclass carrying the `SafeData` marker). Values become safe through `mark_safe()`, `format_html()`, the `|safe` filter, or a filter registered as safe that returns a safe result. The check uses the `__html__` convention, so markup objects from other libraries that implement it are also left alone. Because escaping is conditional on that marker, a value is never escaped twice by the automatic pass: `{{ review.body|escape }}` still produces a single round of escaping. ## Ways output is not escaped - **`{% autoescape off %}...{% endautoescape %}`** turns escaping off inside the block. Per Django's documentation, the effect carries into templates that extend the current one and into templates included within the block. `{% autoescape on %}` turns it back on inside an off region. - **String literals in the template** — for example the argument in `{{ review.title|default:"Untitled & unrated" }}` — are inserted without escaping, as if passed through `safe`. The template author controls them, so write entities yourself. - **Safe values** from Python code or filters, as above. - **Engine configuration**: setting the backend's `autoescape` option to `False` disables it for every template the engine renders, which is appropriate only for engines producing non-HTML output such as plain-text emails. ## What autoescaping does not cover Autoescaping is **HTML escaping**. It is the right encoding for text between tags and for quoted attribute values, and it is not a general guarantee: - A value placed unquoted in an attribute can still break out using spaces. - A value placed in an `href` can still be a `javascript:` URL; escaping does not validate URL schemes. - A value placed inside `<script>` needs JavaScript or JSON encoding (`escapejs`, `json_script`), not HTML entities. Those position-specific rules are the general output-encoding model; the Django-specific point is simply that `{{ }}` gives you HTML encoding and nothing more. ## Practical rules - Leave autoescaping on for HTML templates and never disable it to "fix" double-escaped output; find the value that was escaped twice instead. - Keep every attribute value quoted. - Treat each `|safe`, `mark_safe()` and `{% autoescape off %}` as a reviewed exception, because each one removes the protection for that value.
- Does {{ review.body|escape }} escape the value twice when autoescaping is on?No. The `escape` filter returns a value already escaped and marked safe (it uses `conditional_escape`), and the automatic pass skips safe values, so there is one round of escaping. For deliberate repeated escaping Django provides `force_escape`, which escapes immediately every time.
- A plain-text email template shows & and ' in the message. What is the Django-specific fix?Autoescaping is producing HTML entities for a non-HTML format. Wrap the template body in `{% autoescape off %}...{% endautoescape %}`, or render text emails with an engine configured with `autoescape` set to `False`. Never apply that to HTML templates.
saying these in an interview costs you the question
- Django templates only escape output when you add the |escape filter.
- Autoescaping strips dangerous tags out of the value.
- Autoescaping makes a value safe inside a script tag or href.
- String literals in filter arguments are escaped like variables.
- Autoescaping runs before filters, so filters see escaped text.