A Django middleware that converts HTML responses to Markdown when the client
sends an Accept: text/markdown request header.
django-markdown-middleware supports Python 3.11+ and requires Django 5.2 or later.
$ pip install django-markdown-middlewareAdd the middleware to your Django settings:
MIDDLEWARE = [
...
"markdown_middleware.middleware.MarkdownMiddleware",
...
]The middleware must be placed after any middleware that sets the response
content type (e.g. after CommonMiddleware).
Any client that sends Accept: text/markdown in the request headers will
receive the HTML response body converted to Markdown, with the response
Content-Type changed to text/markdown; charset=utf-8.
The response also includes an X-Markdown-Tokens header containing an
approximate token count of the converted Markdown content (estimated as
len(markdown) // 4).
Only HTTP 200 responses with a text/html content type are converted.
All other responses are passed through unchanged.
To enable caching of converted Markdown responses, set
MARKDOWN_MIDDLEWARE_CACHE_TIMEOUT in your Django settings to the desired
cache duration in seconds:
# Cache converted Markdown responses for 5 minutes
MARKDOWN_MIDDLEWARE_CACHE_TIMEOUT = 300When this setting is absent or None, no caching is performed.
Cache keys have the form markdown_middleware:<path>, for example
markdown_middleware:/api/products/, making them easy to inspect or
manage directly in your cache backend.
To invalidate cached Markdown responses for a specific path, use
invalidate_cache. It removes all cached variants of that path,
including responses with different query strings:
from markdown_middleware import invalidate_cache
# Invalidates /blog/my-post/, /blog/my-post/?page=2, etc.
invalidate_cache("/blog/my-post/")This is useful in post-save signals or management commands when content changes and the cached Markdown must be refreshed.
Set MARKDOWN_MIDDLEWARE_ANONYMOUS_ONLY to False to also convert
responses for authenticated users (defaults to True):
MARKDOWN_MIDDLEWARE_ANONYMOUS_ONLY = FalseSet MARKDOWN_MIDDLEWARE_CACHE_TIMEOUT to the desired cache duration in
seconds for converted Markdown responses. When absent or None (the
default), no caching is performed:
MARKDOWN_MIDDLEWARE_CACHE_TIMEOUT = 300Set MARKDOWN_MIDDLEWARE_CONVERT_OPTIONS to a dictionary of keyword
arguments passed directly to html-to-markdown's ConversionOptions.
When absent or None (the default), the library's defaults are used.
Remove inline SVG elements:
MARKDOWN_MIDDLEWARE_CONVERT_OPTIONS = {"strip_tags": ["svg"]}Remove inline (data URI) images while keeping linked images:
MARKDOWN_MIDDLEWARE_CONVERT_OPTIONS = {"exclude_selectors": ['img[src^="data:"]']}Both options combined:
MARKDOWN_MIDDLEWARE_CONVERT_OPTIONS = {
"strip_tags": ["svg"],
"exclude_selectors": ['img[src^="data:"]'],
}See the html-to-markdown documentation for the full list of available options.
Install uv, then:
$ uv sync --group devRun the tests:
$ make tests