What It Protects


What it protects (and doesn't) #

  • Gates the element's own template-rendered URL: Entries, categories, and custom element types with a template. The field is hidden from the layout designer for assets, users, and global sets, which have no such URL. Placement can't be fully blocked (inline creation, project config), so on a non-gateable element the field warns that a password has no effect.
  • Not element-less routes: Pages rendered by a custom route or a standalone template, with no element behind them, have nothing to hold a password and so can't be gated. Protection is tied to the element it protects, by design. The password lives on the same record as the content.
  • Not static files: Assets are served without Craft in the request, so the gate never runs. Protecting them is a separate problem, the usual approach is a private filesystem with a controller that authorises and streams each file.
  • Not other queries: A protected element's fields shown in a listing, relation, eager-loaded loop, GraphQL, or the Element API are not gated, that's up to your templates (see Filtering listings and Note on GraphQL and the API).
  • Never outputs the password: {{ entry.<handle> }} prints ••••••••, and the value is kept out of the search index and GraphQL schema. Twig can't unwrap it either, templates only ever get the mask. The plaintext is reachable only from Speakeasy's own PHP, which the gate uses to compare.
  • Fail-closed on key loss: If the security key is rotated or lost, existing passwords can't be decrypted and those elements stay locked. The field says so, and stays empty until an editor enters a new password. Saving in the meantime leaves the old value untouched, so the element never falls open and never quietly adopts the unreadable value as its password. The original can't be recovered, so re-enter passwords after a key change.
  • Unlocks live in the visitor's session: They end when the browser closes, and PHP may expire an idle session sooner (session.gc_maxlifetime, often 24 minutes). Unlock duration sets an upper bound on top of that, it can't extend an unlock beyond the session itself, so an unlock lasts for whichever ends first.
  • Rate-limited per IP + password: One unlock covers every element sharing a password, so the attempt budget is shared the same way. Behind a proxy or CDN, make sure Craft is configured to see the real client IP. Rate limiting relies on Craft's cache, so a null/dummy cache driver disables the lockout.