django-suppressor
HTML asset capture and runtime bundling for Django.
Suppresses individual <link> and <script> tags in your templates and replaces them with bundled equivalents at runtime.
Installation
pip install django-suppressor
Rationale
Django templates scatter <link> and <script> tags across base templates, child templates, and includes. Each tag means a separate HTTP request. Existing bundling tools either require you to rewrite all asset references as custom tags or operate as offline build steps disconnected from the template system.
Suppressor takes a different approach: wrap a template region, let Django render it normally, then parse the resulting HTML fragment to find asset tags. Captured tags are removed from the fragment and their contents are concatenated into a single content-addressed bundle served at runtime. Child templates keep writing plain HTML — no special asset registration required.
This is deliberately scoped to explicit template regions rather than full-page rewriting: safer, faster, predictable ordering, and easy to debug.
Quick Start
- Add
suppressorto your installed apps and middleware:
INSTALLED_APPS = [
"django.contrib.staticfiles",
"suppressor",
...
]
MIDDLEWARE = [
...
"suppressor.middleware.SuppressorMiddleware", # near the end
]
- Include the suppressor URLs in your root URL config:
from django.urls import include, path
urlpatterns = [
path("", include("suppressor.urls")),
...
]
- Wrap asset regions in your base template:
{% load suppressor_capture %}
<head>
{% suppressor_emit_css "head" %}
{% suppressor_capture "head" only_css %}
<link rel="stylesheet" href="{% static 'css/base.css' %}">
<link rel="stylesheet" href="{% static 'css/theme.css' %}">
{% block extra_css %}{% endblock %}
{% endsuppressor_capture %}
</head>
<body>
...
{% suppressor_capture "body_js" only_js %}
<script src="{% static 'js/vendor.js' %}"></script>
<script src="{% static 'js/app.js' %}"></script>
{% block extra_js %}{% endblock %}
{% endsuppressor_capture %}
{% suppressor_emit_js "body_js" %}
</body>
Child templates keep using plain <link> and <script> tags inside the inherited blocks. Suppressor captures them, bundles by type, and emits a single content-addressed URL per region.
- Run
collectstaticbefore use — suppressor reads from staticfiles storage, not finders:
python manage.py collectstatic
Configuration
Bundling is controlled by SUPPRESSOR_ENABLED, which defaults to not DEBUG. Override it to test bundling locally:
SUPPRESSOR_ENABLED = True
Bundle content is stored using Django's cache framework under the suppressor alias. A FileBasedCache default is auto-configured if you don't define one. To use a different backend:
CACHES = {
...
"suppressor": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "127.0.0.1:11211",
},
}
Limitations
- Only processes explicitly wrapped template regions (no full-page rewriting)
- Bundles plain external
<script src>/<link rel="stylesheet">and plain inline<script>/<style> - Any tag carrying extra attributes (
async,defer,type="module",nomodule,integrity,media/title/disabled,nonce,crossorigin,referrerpolicy,id,data-*, …) or a cross-origin / non-http(s)URL is preserved unchanged - CSS relative
url(...)references are rewritten to absolute paths; bare@import "file.css"(withouturl()) is not rewritten StreamingHttpResponseis not processed- See also "TODO" section below.
Tested against
CI runs the test suite across Python 3.10–3.14 and Django 4.2, 5.0, 5.1, 5.2 and 6.0 (only supported combinations of each).
TODO
- improve template ergonomics
- preserved (ignored) tags can reorder CSS cascade / JS execution vs the bundle
Status
Under development. See SPEC-0-1-0.md for the original design specification and rationale — it is the historical founding spec the project evolves from, not a description of current behavior.
Metadata
Release files for django-suppressor 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_suppressor-0.2.0.tar.gz | 36.1 kB | With provenance | Details for django_suppressor-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_suppressor-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for django_suppressor-0.2.0-py3-none-any.whl |
Total release size: 51.5 kB
Release files / django_suppressor-0.2.0.tar.gz
| Download URL | django_suppressor-0.2.0.tar.gz |
|---|---|
| Size | 36.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6e6c6ee2730ecc412512f580d973b0151523e95ffa58be5072070bfdb76800b3
|
|
BLAKE2b-256 checksum How to use checksums |
8cb8a994599803fac1024074069185ee28b9c93718d29b9efd4172dbf5d59efb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 26, 2026.
Transparency logRelease files / django_suppressor-0.2.0-py3-none-any.whl
| Download URL | django_suppressor-0.2.0-py3-none-any.whl |
|---|---|
| Size | 15.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e9ffb948aedd084860c4e755c79db5f7e188c439ed5d9802e2938bf8899257b5
|
|
BLAKE2b-256 checksum How to use checksums |
c38e7258459078822fc9895e3b2f04c62dfe31626885945d9182f8d9ef3782f8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 26, 2026.
Transparency logSecurity for django-suppressor 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for django_suppressor-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 26, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.