Passing HTML to template using Flask/Jinja2
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
Passing HTML into a Flask template is easy mechanically, but the security implications matter more than the syntax. Jinja2 escapes variables by default, so the real question is not just how to render HTML, but when a value is trusted enough to be rendered as markup instead of plain text.
Why Jinja2 Escapes HTML by Default
In a normal Flask template, this expression is escaped:
If body contains "<strong>Hello</strong>", the browser will display the tags as text instead of rendering bold output. That behavior is intentional. Autoescaping protects your app from cross-site scripting, which is exactly what happens when untrusted content is injected into a page as live HTML.
A small Flask example shows the default behavior:
Visit the route and you will see the literal markup characters, not bold text. That is usually what you want for raw user input.
Rendering Trusted HTML Intentionally
If the HTML was generated by your own application and you know it is safe, you can mark it as trusted. The most direct approach is Markup from markupsafe.
Template:
Because bio is marked safe, Jinja2 will render it as HTML rather than escaping it.
You can also use the safe filter in the template:
That works, but in many codebases it is better to make the trust decision in Python rather than sprinkling |safe through templates. It is easier to review and easier to test.
Sanitizing User-Provided HTML
If the content comes from users, a database field, or a CMS, “safe” and “works” are not the same thing. Untrusted HTML should be sanitized before rendering.
A common option is bleach:
This keeps allowed formatting while stripping dangerous tags and attributes. That is a much safer pattern than marking user data as safe directly.
Passing Structured Data Is Often Better
A lot of applications do not need to pass HTML at all. If the content is really data, pass the data and let the template decide how to render it.
For example, instead of assembling an HTML list in Python:
pass structured values:
Template:
This is safer, easier to maintain, and keeps presentation logic where it belongs.
When Markup Is Appropriate
Markup is appropriate when your code generates the HTML itself or when you have sanitized the content already. It is not a license to bypass escaping because “the string looks harmless.”
A good rule is:
- trusted application-generated markup can be wrapped in
Markup - user-generated markup should be sanitized first
- plain text should be left to autoescape normally
That rule keeps the default safe behavior intact while still allowing rich content when the data model really needs it.
Common Pitfalls
The biggest mistake is using |safe on raw user input. That turns a formatting problem into a security bug.
Another common problem is building large HTML fragments in Python when the data should have been passed as structured values and rendered in Jinja2.
Developers also sometimes assume that “internal” data is automatically safe. If the content ultimately came from an editor, import job, or database field, it still needs a trust decision.
Finally, make the safe-or-unsafe choice in one place. Mixing Markup in some views and |safe in some templates makes audits harder.
Summary
- Jinja2 escapes variables by default to prevent cross-site scripting.
- Use
Markupor|safeonly for content you trust or have sanitized. - Sanitize user-provided HTML before rendering it as markup.
- Prefer passing structured data to templates instead of assembling HTML in Python.
- Treat HTML rendering as both a template concern and a security decision.
Related reading
- passing supplementary parameters to hyperopt objective function
- PATH issue with pytest 'ImportError No module named ...
- pdb cannot break in another thread?
- Peak-finding algorithm for Python/SciPy
- passing multiple arguments to promise resolution within setTimeout
- passing parameters to Node.js async waterfall
- Perform commands over ssh with Python
- Permanently add a directory to PYTHONPATH?
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.