Think of your module as a building with no doors yet. The PHP code inside might be perfectly functional, but nobody outside can reach it — there's no way for a URL to lead a visitor to it. A .routing.yml file is how you cut doors into that building: each entry maps one URL path to the exact controller method that should run when someone visits it. This lesson walks through building one from scratch, using the real routing file from Drupal's own Examples project as a working reference.
What you'll learn in this lesson
- Where a
.routing.ymlfile lives and how Drupal discovers it automatically - The minimum shape every route needs: a name, a path, a controller, and an access rule
- How
_titlesets the page's heading without any extra code - Why route names use a
module_name.somethingconvention
Where the file goes, and how Drupal finds it
A routing file is named <module_name>.routing.yml and lives at the root of your module's own folder — right alongside its .info.yml file, not inside any subdirectory. Drupal scans every enabled module for a file matching this exact naming pattern at bootstrap and caches what it finds. There's no separate registration step: create the file with the right name, clear the cache, and its routes exist.
A real routing file, read line by line
Here's an actual working example from Drupal's Examples module (page_example.routing.yml), trimmed to its simplest route:
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'
The route name
page_example.simple is this route's unique identifier across the entire site — not a URL, just a name. By convention it starts with your module's machine name followed by a dot and a short description, which guarantees it can never collide with a route defined by another module. You'll reference this exact name later whenever you need to generate a link to this page programmatically.
path
path: '/examples/page-example/simple'
This is the actual URL a visitor types or clicks. It's always written relative to the site root, always starts with a leading slash, and can include dynamic placeholders in curly braces — you'll meet those in the next lesson.
_controller
_controller: '\Drupal\page_example\Controller\PageExampleController::simple'
This is the fully-qualified class name and method that actually runs when the route matches — ClassName::methodName. Drupal instantiates the class and calls the method, and whatever that method returns becomes the page. You'll build one of these classes in the next few lessons.
_title
_title: 'Simple - no arguments'
Sets the page's <title> tag and heading automatically — no need to build this yourself inside the controller. If the title needs to be dynamic (based on which entity is being viewed, for example), you can use _title_callback instead to point at a method that computes it.
requirements
requirements:
_permission: 'access simple page'
This is the access rule — and it's mandatory. Every route needs at least one requirement, whether that's a named permission like this one, a role check, or _access: 'TRUE' to make the page open to everyone. Skip this section entirely and Drupal refuses to load the route at all.
drush cache:rebuild (or clearing caches from the admin UI) — Drupal hasn't re-scanned your module's routing file yet.Quick check: you write a route with no
requirementskey at all and try to visit it. What happens? If you said "Drupal throws an error and refuses to define the route," you're right —requirementsisn't optional decoration, it's how Drupal knows whether anyone is allowed to reach this door at all.
Key takeaways
- A routing file is named
<module_name>.routing.ymland lives at your module's root — Drupal auto-discovers it by that exact naming convention. - Every route needs, at minimum: a unique name, a
path, a_controller, and arequirementsblock defining who can access it. - Route names follow a
module_name.descriptionpattern to guarantee uniqueness across every module on the site. _titlehandles a static page title for free; use_title_callbackwhen the title needs to be computed dynamically.- Changes to a routing file require a cache rebuild before they take effect — a 404 on a route you just added almost always means the cache is stale.
Coming up next
Real routes rarely stay this static — most need to accept part of the URL as input, like an ID or a username. The next lesson, Specifying Route Paths, covers exactly that: using curly-brace placeholders in a path so a single route can handle a whole family of URLs.