Django 5.1: make private pages private by default
Django 5.1 introduced optional login enforcement across views. Use it for private applications while keeping public exceptions and record permissions explicit.
A new reporting page should not become public because its author forgot a decorator. For an application whose normal audience is signed-in staff or customers, requiring login by default is a sensible starting policy. Django 5.1 supplied an optional mechanism for that policy. It still leaves the application responsible for deciding which records each user may see.
Looking back from November 2026, the useful change in the 7 August 2024 release was the direction of the exception. A private application could make public access deliberate. Upgrading alone did not change the access policy: the new middleware had to be configured.
A forgotten declaration changes meaning
With view-by-view login requirements, an omitted declaration can mean anonymous access. In an internal application, that is often the wrong failure mode. A developer adds a page, reviews its calculation and template, and overlooks the unrelated requirement that visitors must first sign in. The application may render exactly what was implemented while exposing more than the business intended.
Under a default login requirement, the omitted declaration instead leaves the page behind login. That reduces one particular mistake. It does not establish that the report is appropriate for every account, or that the report's data query respects customer boundaries. Those decisions need explicit implementation and review.
The Django 5.1 release notes introduce LoginRequiredMiddleware and the login_not_required decorator together. Treat them as a policy and its exceptions. A code review can then ask why a view is public, rather than relying on every private view carrying a repeated reminder.
Configure the boundary you actually have
Middleware order matters. The session middleware must precede AuthenticationMiddleware, and the login requirement belongs after authentication has populated the user. The tagged Django 5.1 middleware source shows the relevant behavior: authenticated users continue; an anonymous request can continue when the view carries the opt-out attribute; other anonymous requests are redirected to login.
That redirect is a browser-oriented response. An integration expecting a machine-readable error may receive a login page instead. Before applying the middleware across a mixed application, identify endpoints used by other systems and decide their authentication contract. Exempting such an endpoint from this browser policy must leave its own verification intact.
For example, a payment callback may legitimately arrive without a staff session. This is an illustrative design case, not an account of a client system. Making the callback reachable does not make arbitrary payment messages trustworthy. Its separate checks must establish the sender and validate the event before changing an order.
Keep the way in reachable
A custom login view needs a deliberate exception; otherwise the visitor can be redirected back toward the page they were already trying to reach. Password recovery and other anonymous entry routes deserve the same inventory. The built-in LoginView in the 5.1 authentication views source already has the relevant exemption. Do not assume every custom replacement inherits that behavior.
The 5.1 decorator source makes the opt-out's scope clear: login_not_required sets the view's login_required attribute to false. It does not issue a credential or grant a permission. A public label is a route-policy decision, not evidence that the view is harmless.
Review exceptions by purpose. A public help article and a signed callback have different reasons for anonymous reachability. Keeping those reasons visible helps a future maintainer avoid copying the exception onto a sensitive report. The aim is a small, understandable public surface, rather than an impressive count of decorated functions.
A colleague is still the wrong customer
A proposed acceptance check should include an anonymous visitor, a permitted account and a signed-in account belonging to another customer. The first case examines the login boundary. The other cases examine the business boundary. A successful redirect for the anonymous visitor says nothing about whether the wrong customer can change an identifier and obtain another invoice.
Inspect downloads and exports alongside the page that links to them. Hiding a link does not define access to its destination. These are proposed review checks, not tests performed for this article. A specific implementation should demonstrate the important access decisions without creating a large test suite that merely repeats configuration.
This is also why choosing a session or token format would answer a different question. Here the decision is which views require a recognized user by default, followed by which data that user may access. A valid identity remains only one input to that decision.
Public sites have a real objection
For a publication or public catalogue, most pages are intentionally anonymous. Adding exceptions everywhere can create maintenance work and obscure the few private functions. Keeping explicit login requirements on a bounded account area may be clearer. Default privacy is a recommendation for applications whose ordinary content is private, not a universal rule for Django sites.
For a mostly private application, begin a custom software review with the view inventory and the anonymous entry routes. Then examine record permissions separately. Adopt the middleware when that policy fits, preserve the public paths that have a reason to exist, and require evidence that a signed-in user still encounters the correct data boundary.
Paul