Wagtail 8.0 release notes

August 25, 2026

What’s new

Wagtail REST API v3 (preview)

Wagtail 8.0 introduces a preview of a new v3 API, built on Django Ninja and type hints. The v2 API was designed for headless site and other data publication needs. v3 serves similar use cases, and adds support for CMS operations on top: authenticated clients can create, edit, publish and unpublish, move, copy, and revert content, and manage revisions, across pages, sites, locales, redirects, images, documents, and API-enabled snippets. You can create new pages with writable StreamField content, rich text as HTML or Markdown. The API auto-generates precise OpenAPI 3.1 schemas based on your project’s models, and uses API token authentication tied to user accounts. See the v3 API documentation for full details.

The v3 API is released as a preview to signify we want to adapt it based on feedback: it may change in backwards-incompatible ways in any release until stabilised. We welcome feedback via discussions, feature requests and bug reports. This feature was developed by Thibaud Colas and Sage Abdullah.

Custom base page models

This release introduces the ability to swap out the base Page model with a custom project-specific model. Any fields and methods defined on this model will be shared by all page types. This feature is only supported for newly-created projects, and certain add-on packages may not yet be compatible with it (see Adapting reusable apps to support custom base page models). This feature was developed by Matt Westcott.

Permission policy registry

A permission policy for every model managed by Wagtail is now registered to a global permission policy registry. The registry allows you to retrieve the permission policy of a model from anywhere in the code, which can be useful for performing permission checks outside of a view’s request-response cycle, such as in a background task. It can also be used to implement custom permission logic, including for Wagtail’s built-in models.

For more details, refer to the Permissions reference documentation. This feature was developed by Sage Abdullah.

Django 6.1 support

This release introduces formal support for Django 6.1.

Guide website improvements

The Wagtail user guide has been updated to use a new versioning scheme, better search and navigation, and better support for Right-to-Left (RTL) languages.

Thank you to Raghad Dahi for leading this work. For more information, read her project report: Streamlining content ops with LLMs: Wagtail user guide.

Automated dependency management

We now provide SBOM exports of our dependency graph in SPDX JSON format. This complements a number of improvements to dependency management, implemented by Sage Abdullah, Dan Braghis, and Thibaud Colas:

  • Automated testing with latest versions of all dependencies

  • Lockfiles and pinned dependencies for reproducible CI builds

  • zizmor for GitHub Actions quality checks

  • Automated dependency updates via Renovate

Security fix: Improper restriction handling on Pages admin API

The internal Pages admin API incorrectly returned page fields without access control when they were declared in api_fields. A user with access to the Wagtail admin could use this API to fetch draft and live page fields’ contents that are part of api_fields on the base page model (title, slug, seo_title, search_description), as well as all custom fields declared in api_fields.

Many thanks to xuliang@QAX for reporting this issue. For further details, please see security advisory GHSA-3vrh-m9w7-v94f.

Security fix: Identification of documents by SHA1 hash

By passing specific HTTP headers to the document serve URL endpoint, an attacker was able to determine whether a document with a given ID matched a specified SHA1 hash, regardless of any permission restrictions on the document or knowing its filename. This could allow an attacker to determine whether a document with a specific known hash is present in the Wagtail document library.

Many thanks to Anand Himanshu for reporting this issue. For further details, please see security advisory GHSA-92hv-j533-69wc.

Security fix: Improper restriction handling on descendant collections in Documents and Images API

The Documents and Images API incorrectly listed items in descendants of private collections, which should inherit the view restrictions defined on their ancestors. A user with access to the API could see the filename and name of documents and images in these descendant collections.

Many thanks to Ta Duc Thien for reporting this issue. For further details, please see security advisory GHSA-c2xx-cjmh-9q8f.

Security fix: Improper permission handling when copying snippets

A CMS user with “add” permission over a snippet model, but not “change” or “view” permission, could copy an existing snippet that they do not have access to, allowing them to view its contents.

Many thanks to tinyb0y for reporting this issue. For further details, please see security advisory GHSA-x5cx-w6p2-mxf2.

Security fix: Improper restriction handling on Page translation API endpoint

A CMS user with the “submit translations” permission, could use the Admin API’s “copy for translation” endpoint to copy an existing page that they do not have edit access to, allowing them to view its contents.

Many thanks to tinyb0y for reporting this issue. For further details, please see security advisory GHSA-jm5p-837g-rv8g.

Other features

  • Add customizability for all remaining page views to PageViewSet (Sage Abdullah)

  • Set FieldPanel(required_on_save=True) for AbstractFormField’s field_type field (Alex Tomkins)

  • Handle pasting of multiple tags separated by newlines or commas (Matthias Brück)

  • Make Button component render a <button> element when no URL is supplied (Nayeli De Jesus, LB (Ben Johnston), Sage Abdullah)

  • Show site name in site switcher dropdown in site settings editor (Jack Morgan)

  • Improve clarity of different task states in workflow details dialog (Dan Braghis)

  • Add submenu_hook attribute to ViewSetGroup for collecting submenu items registered via a hook (Hunzlah Malik, MariyaOT, Sage Abdullah)

  • Respect ViewSet.menu_order when used in a ViewSetGroup (Sage Abdullah)

  • Use a more accessible label for blank values of ChoiceBlock (Sage Abdullah)

  • Use a more accessible label for blank values of dropdowns in locked page report filters (Sage Abdullah)

  • Make audit log messages for scheduling/unscheduling generic (Sage Abdullah)

  • Stop converting AVIF and WebP images to PNG by default (Thibaud Colas)

  • Add rich text serializing to HTML support in the v2 API (Thibaud Colas)

  • Expose the block id as a template context variable when rendering children of StreamBlock and ListBlock (Sage Abdullah)

  • Add extension blocks for image and document stats in the edit view templates (Preeti Singh)

Bug fixes

  • Make SnippetChooserViewSet.widget_class a class instead of an instance (Amrinder Singh, Sage Abdullah)

  • Prevent an error when approving a workflow that has been cancelled in a different session (Kailesh)

  • Accommodate multilingual sites in skipping the “choose parent” step when creating a new page from a flat page listing (Amrinder Singh)

  • Prevent error when moving a page to a destination with missing translations and WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE = True (Sahil Kumar)

  • Handle Pillow detecting decompression bombs when validating image pixel count (Jake Howard, Sage Abdullah)

  • Use canonical URL for TED oEmbed provider (Marquis Nobles)

  • Prevent stale content types from breaking audit log message formatting (Thibaud Colas)

  • Prevent icons from capturing clicks inside buttons (Maciek Baron)

  • Ensure form field clean_name is consistently set on form pages if autosave runs prematurely (Joey Jurjens)

  • Enforce ‘choose’ permission on image chooser select-format view (Matt Westcott)

Documentation

  • Add reference documentation entry for SnippetChooserViewSet (Sage Abdullah)

  • Fix panel classname and deprecated usage of format_html in construct_homepage_panels example (Andreas Nüßlein)

  • Update django-ninja code examples to use new Meta class implemented in django-ninja V1 (Jack Whitworth)

  • Document request.in_preview_panel to explain its use (Raghad Dahi)

  • Update image format docs to encourage more intentional conversions (Thibaud Colas)

  • Clarify Wagtail XSS protections for uploaded documents (Thibaud Colas)

  • Add section on customizing StreamField block API output with get_api_representation (Pranith Beeram)

  • Removed now-deprecated USE_L10N setting from internationalization and project setup documentation (Piyush Bhakuni)

  • Add a new images overview page (Dmitry Khlopyev)

Maintenance

  • Rename request argument to cache_object in Page._get_site_root_paths() for correctness (Kailesh)

  • Fix blank choice tests for Django 6.1 (Sage Abdullah)

  • Ignore EMAIL_BACKEND setting deprecation on Django 6.1 when running project template tests (Sage Abdullah)

  • Add one and not match support to RulesController (LB (Ben Johnston))

  • Refactor pages’ function-based views to class-based views (Sage Abdullah)

  • Enable flake8-bandit security-focused linting rules (Storm Heg)

  • Add support for latest Azure SDK for frontend cache invalidation (Sage Abdullah, Tomasz Knapik)

  • Update guide.wagtail.org links to new versioning scheme (Raghad Dahi)

  • Format Python code in documentation using ruff (Thibaud Colas)

  • Use Ubuntu 26.04 ARM runner for GitHub Actions workflows (Storm Heg)

Upgrade considerations - removal of deprecated features from Wagtail 6.4 - 7.3

Features previously deprecated in Wagtail 6.4, 7.0, 7.1, 7.2, and 7.3 have been fully removed:

  • The construct_wagtail_userbar hook now receives a third argument page in addition to request and items; hook functions that only accept two arguments will now fail.

  • The JavaScript functions buildExpandingFormset and initPrefillTitleFromFilename are removed.

  • The settings TAG_LIMIT and TAG_SPACES_ALLOWED are replaced by WAGTAIL_TAG_LIMIT and WAGTAIL_TAG_SPACES_ALLOWED.

  • Custom listing views using the template wagtailadmin/generic/index.html must now provide a breadcrumbs_items context variable.

  • The template wagtailadmin/pages/_editor_js.html is removed.

  • The PageListingButton, SnippetListingButton and UserListingButton classes are removed in favour of ListingButton and Button.

  • The function wagtail.admin.signals.init_new_page is moved to wagtail.signals.init_new_page.

  • The module wagtail.telepath is moved to wagtail.admin.telepath.

  • The module wagtail.widget_adapters is moved to wagtail.admin.telepath.widgets.

  • The JavaScript include wagtailadmin/js/telepath/telepath.js is removed.

  • The INDEX option on WAGTAILSEARCH_BACKENDS is replaced by INDEX_PREFIX (for example, "INDEX": "mysite" now becomes "INDEX_PREFIX": "mysite_").

  • Userbar items now follow the template component API and do not provide a render method.

  • The resetValue property is removed from TeleportController.

For additional details on these changes, see:

Upgrade considerations - changes affecting all projects

AVIF and WebP images are no longer converted to PNG by default

Browser support for AVIF and WebP is now solid enough that Wagtail no longer converts images in these formats to PNG when no output format is specified. Rather than relying on this setting, we recommend controlling output formats explicitly with the format-* filter or picture template tag. See Customizing output formats for more details.

If you need to retain the previous behavior as-is, add the following to your settings:

WAGTAILIMAGES_FORMAT_CONVERSIONS = {
    "bmp": "png",
    "heic": "jpeg",
    "avif": "png",
    "webp": "png",
}

Upgrade considerations - deprecation of old functionality

Support for legacy versions of azure-mgmt-cdn, azure-mgmt-frontdoor, and azure-mgmt-resource packages will be dropped

If you are using the front-end cache invalidator module (wagtail.contrib.frontend_cache) with Azure CDN or Azure Front Door, the following packages need to be updated:

  • For Azure CDN: upgrade azure-mgmt-cdn to version 13 or above

  • For Azure Front Door: upgrade azure-mgmt-frontdoor to version 1.1 or above

In addition, if you do not set SUBCRIPTION_ID and thus rely on azure-mgmt-resource to retrieve the default subscription, you should install the azure-mgmt-subscription package instead of (or in addition to) azure-mgmt-resource.

Support for older versions will be dropped in a future release.

Upgrade considerations - changes affecting Wagtail customizations

Support for custom base page models in add-on packages

Reusable apps that work with the Page model are likely to require updating in order to be usable on projects that use the new custom base page model feature. This process is detailed at Adapting reusable apps to support custom base page models.

Custom permission policy in viewsets must be registered separately

If you use the Generic views and have a ModelViewSet, SnippetViewSet, or ChooserViewSet that defines a custom value for the (previously-undocumented) permission_policy, you must now register the permission policy separately.

Registering a permission policy can be done by calling register_permission_policy(Model, <policy_instance>) at the top of the model app’s wagtail_hooks.py.

# wagtail_hooks.py
from wagtail.permissions import register_permission_policy
from .models import MyModel


register_permission_policy(MyModel, my_custom_policy_instance)
...  # More customizations

Alternatively, you can also put the registration in the app’s AppConfig.ready().

# apps.py
class MyAppConfig(AppConfig):
    ...

    def ready(self):
        from wagtail.permissions import register_permission_policy
        from .models import MyModel

        register_permission_policy(MyModel, my_custom_policy_instance)

If you do not register the permission policy explicitly, Wagtail will automatically register the viewset’s permission_policy, and a deprecation warning will be raised. This support will be removed in a future release.

This change does not affect viewsets that do not have a custom permission_policy.

SnippetChooserViewSet.widget_class is now a class

The SnippetChooserViewSet.widget_class attribute now correctly returns a widget class instead of an instance, consistent with ChooserViewSet.widget_class. This change may require updates to any customizations that relied on the previous behavior, such as an override in a SnippetChooserViewSet subclass that uses super().widget_class.

ViewSet.menu_order is now respected when used in a ViewSetGroup

ViewSets that are registered as part of a ViewSetGroup now have their menu_order respected instead of always using its position in ViewSetGroup.items.

If you rely on the previous behavior that used the items ordering, remove menu_order in your ViewSet definition.

Setting submissions_list_view_class on form page models no longer works

The submissions_list_view_class attribute on the form builder’s AbstractForm model, previously documented at Customize form submissions listing in Wagtail Admin, is no longer directly usable. This is because the wagtail.contrib.forms.views module can no longer be imported at model load time without introducing a circular import, and so there is no way to refer to a subclass of SubmissionsListView within a model definition. Instead, the get_submissions_list_view_class method can be overridden to achieve the same result. First, the definition of the SubmissionsListView subclass, and the import of SubmissionsListView, should be moved to a separate views module within the app. Then, the attribute assignment:

class FormPage(AbstractEmailForm):
    # ...
    submissions_list_view_class = CustomSubmissionsListView

can be replaced with:

class FormPage(AbstractEmailForm):
    # ...
    def get_submissions_list_view_class(self):
        from myapp.views import CustomSubmissionsListView

        return CustomSubmissionsListView

Upgrade considerations - changes to undocumented internals

request argument to Page._get_site_root_paths is now cache_object

The request argument to the undocumented method Page._get_site_root_paths() is renamed to cache_object to reflect the fact that it is not always a request object, but may be any object that can be used for caching purposes.

If you pass request as a positional argument, no changes are needed. If you pass request as a keyword argument to this method, you will need to update the argument name to cache_object in your code or turn it into a positional argument.

Passing a request keyword argument will continue to work for now and raise a deprecation warning, but support for this will be removed in a future release.