Applying permissions to routes in .routing.ymlfor Drupal 8 , 9 , 10 , and 11

Last updated :  

Declaring a permission is only half the story — a permission that no route ever checks is just an unused checkbox sitting on the permissions page. This lesson connects the two halves: taking a permission machine name from .permissions.yml and actually wiring it into a route so Drupal enforces it on every single request, before your controller ever runs.

What you'll learn in this lesson

  • The _permission requirement key, and exactly when Drupal checks it
  • What happens to a visitor who fails the check
  • Why access checking happens before your controller code runs at all
  • How to require more than one permission, or leave a route open to everyone

The source file

Real routes from Drupal core's Examples project (page_example.routing.yml), each with a different access requirement:

# Access to this path is not restricted.
page_example.description:
  path: '/examples/page-example'
  defaults:
    _controller: '\Drupal\page_example\Controller\PageExampleController::description'
    _title: 'Page Example'
  requirements:
    _permission: 'access content'

# Access to this path requires "access simple page" permission.
page_example.simple:
  path: '/examples/page-example/simple'
  defaults:
    _controller: '\Drupal\page_example\Controller\PageExampleController::simple'
    _title: 'Simple - no arguments'
  requirements:
    _permission: 'access simple page'

How it works

_permission is checked before the controller runs

The routing system resolves the URL to a route, then checks every requirement under that route's requirements key — _permission among them — before it ever instantiates or calls the controller class. If the current user doesn't hold the named permission, the controller method's code never executes at all; the visitor gets a 403 Access Denied response straight from the routing layer. This matters for security: you should never rely on a check inside the controller as your only line of defense, because a route with no _permission requirement is reachable by literally everyone, controller code notwithstanding.

Every route needs an explicit access requirement — there's no safe default

_permission: 'access content' is Drupal core's own broad, "anyone who can view the site" permission — using it is a deliberate choice to leave a route open, not an oversight. Compare that to _permission: 'access simple page', a permission this specific module defined and that an administrator must explicitly grant to a role. Drupal has no implicit default here: if you forget the requirements key entirely, the router throws an error rather than silently allowing or denying access, forcing you to make the decision explicitly.

Requiring more than one permission

requirements:
  _permission: 'access simple page+access arguments page'
The + means OR, not AND. This is the detail that trips people up: 'access simple page+access arguments page' grants access to a user who holds either permission, not one who holds both. There's no built-in "require all of these" syntax for _permission — if you genuinely need every one of several permissions, use _custom_access with a callback that checks each explicitly.

Quick check: a route has no requirements key at all. What happens when someone visits it? (Answer: nothing works — Drupal's route collector throws an exception at cache-rebuild time, because every route is required to declare an access requirement. There's no such thing as an accidentally-public route from a missing key; the system fails loudly instead.)

Key takeaways

  • _permission under a route's requirements key is checked by the routing system itself, before the controller ever runs.
  • A failed permission check returns a 403 response automatically — you never write that logic yourself.
  • Separate multiple permission names with + to mean OR; there's no built-in AND — use _custom_access for that.
  • Every route must declare an access requirement; there's no implicit "open to everyone" default.

Coming up next

Route-level _permission checks cover the common case well, but sometimes access depends on more than a simple named permission — the specific entity being viewed, business logic, a combination of conditions. The next lesson, Permissions Programmatically, covers checking access from inside your own PHP code with user_access() and AccessResult, for exactly those cases.