In Apache httpd 2.4, what is the difference between the <Directory>, <Location> and <Files> containers, and in what order are they merged when several apply to the same request?
answer
- different keys: path, filename, URL
- URL rules are merged last
- later section wins the conflict
- a proxied request has no file
- specificity does not decide it
basics
~20 s<Directory> and <Files> scope rules by filesystem path and filename; <Location> scopes by URL. Apache merges them in a fixed order — Directory, then DirectoryMatch, then Files, then Location, then <If> — with later sections overriding earlier ones.
solid answer
~40 sThe three containers select on different things. `<Directory>` applies to a filesystem directory and everything below it; `<Files>` applies to filenames, anywhere or within an enclosing `<Directory>`; `<Location>` applies to the URL path, before and independently of any mapping to disk. When more than one applies, Apache merges them in a defined sequence — `<Directory>` (non-regex, shortest path first), then `<DirectoryMatch>`, then `<Files>`/`<FilesMatch>`, then `<Location>`/`<LocationMatch>`, then `<If>` — and later sections win on conflict. Two consequences matter in production. First, `<Location>` always beats `<Directory>` for the same request, so an access rule you thought you tightened in `<Directory>` can be reopened by a `<Location>` elsewhere. Second, a proxied URL has no filesystem path at all, so `<Directory>` never matches it — protecting a `ProxyPass`-ed path requires `<Location>` (or `<Proxy>`).
code
apacheconf · 16 lines# Filesystem-backed content: guard where it lives
<Directory "/srv/www/app">
Require all granted
</Directory>
<Files ".env">
Require all denied
</Files>
# Proxied URL space: <Directory> would never match this request
<Location "/admin/">
Require ip 10.0.0.0/8
</Location>
ProxyPass /admin/ http://127.0.0.1:8080/admin/
ProxyPassReverse /admin/ http://127.0.0.1:8080/admin/go deeper
Know that <Directory> talks about folders on disk and <Location> talks about URL paths, and that picking the wrong one means your rule silently does nothing.
Reproduce the merge sequence — Directory, DirectoryMatch, Files, Location, If — and explain that later stages override earlier ones per directive rather than replacing whole blocks.
Demonstrate that you verify access control by request rather than by reading config, and that you know a proxied path is invisible to <Directory>, which is how open admin consoles happen.
Set the convention: one container type per kind of surface, access rules colocated rather than scattered across includes, and a test that proves a denied caller gets a 403 in CI, because merge order cannot be reviewed reliably by eye.
## Three coordinate systems Apache resolves a request in phases: it maps a URL to a resource, then serves it. The containers hook different phases, which is why they select on different keys. - `<Directory "/srv/www/app">` — a **filesystem** path, applying to that directory and everything beneath it. - `<Files "config.php">` — a **filename**, matched against the basename of the resource, either globally or inside an enclosing `<Directory>`. - `<Location "/admin">` — the **URL path** as it arrived, evaluated without reference to any file. A fourth, `<Proxy>` / `<ProxyMatch>`, scopes by the destination URL for proxied requests. ## The merge order When several sections apply, Apache applies them in this sequence, later overriding earlier: 1. `<Directory>` (non-regex) and `.htaccess`, with directories processed shortest path to longest 2. `<DirectoryMatch>` and `<Directory ~>` 3. `<Files>` and `<FilesMatch>` 4. `<Location>` and `<LocationMatch>` 5. `<If>` Sections in a `<VirtualHost>` are applied after the corresponding main-server sections, so a vhost can override a global default. Within one group, sections are applied in the order they appear in the configuration — which, once includes are involved, means the order files are read. The practical summary is short: **URL-space rules override filesystem rules.** If a `<Directory>` sets `Require ip 10.0.0.0/8` and a `<Location>` matching the same request sets `Require all granted`, the request is granted. Merging is per-directive, not whole-block, so a `<Location>` that mentions only one directive does not wipe out unrelated settings inherited from `<Directory>`. ## The trap that actually bites ```apacheconf # WRONG: never applies. There is no such directory on disk. <Directory "/admin"> Require ip 10.0.0.0/8 </Directory> ProxyPass /admin/ http://127.0.0.1:8080/admin/ ProxyPassReverse /admin/ http://127.0.0.1:8080/admin/ ``` A proxied request is never mapped to a file, so no `<Directory>` section is ever selected for it, and `<Directory "/admin">` is not even a valid filesystem path in most layouts. The block sits in the config looking like a control, passes `configtest`, and protects nothing. The admin console is world-open. The correct form scopes by URL: ```apacheconf <Location "/admin/"> Require ip 10.0.0.0/8 </Location> ``` The general rule: **guard proxied and handler-generated URLs with `<Location>`; guard files on disk with `<Directory>`.** ## Why `<Location>` is dangerous for filesystem resources The converse mistake is using `<Location>` to protect files. URL space and filesystem space are not one-to-one: aliases, case-insensitive filesystems, trailing dots, and alternate encodings can all reach the same file through a URL that your `<Location>` pattern does not match. Filesystem-backed content should be protected where it lives, in `<Directory>` — that is one boundary every path eventually funnels through. ## Regex variants and precedence `<DirectoryMatch>`, `<FilesMatch>` and `<LocationMatch>` take regular expressions, and their non-regex counterparts take literal prefixes (with shell-style wildcards for `<Files>`). Note that the ordering above is not "specific beats general": a broad `<LocationMatch "^/">` applied late still overrides a precise `<Directory>` earlier in the sequence. Reason about the merge stage first, specificity second. ## `<If>` and the last word `<If>` sections are evaluated last, using expression syntax over request properties. They therefore override everything before them, which makes them convenient and easy to misuse — an `<If>` in an unrelated file can quietly reopen a restriction set carefully elsewhere. ## How to verify rather than assume The merge is hard to hold in your head across dozens of included files. Verify empirically: request the protected URL from an address that should be denied and confirm the 403, and re-run that check after config changes rather than reading the file and believing it. `apachectl -S` shows which vhost owns the request; `apachectl configtest` proves only that syntax parses, never that a rule applies to the resource you intended.
- A team protected a ProxyPass-ed /admin path with a <Directory> block and the config passes configtest. What is the actual exposure?None of the restriction applies. Proxied requests are never mapped to the filesystem, so no `<Directory>` section is selected and the block is inert — while looking exactly like a control in review. The admin surface is open to anyone who can reach the vhost. Rewrite it as `<Location "/admin/">`, then verify by requesting the URL from a denied address and confirming a 403.
- Given the merge order, can a <Location> section loosen a restriction set in a <Directory> section?Yes. `<Location>` is merged after `<Directory>`, so for a request both match, the Location wins on any directive they both set — including access control. Specificity is irrelevant; stage order decides. This is why scattering authorization across container types across many included files produces surprises, and why access rules for one surface belong in one place.
- Why is <Location> a poor choice for protecting sensitive files on disk?Because many URLs can map to one file. Aliases, case-insensitive filesystems, encoding variants and path quirks give attackers URL forms your pattern may not match, while the file itself has exactly one filesystem location. Guard it with `<Directory>` or `<Files>`, which sit at the point every request must pass through after mapping.
saying these in an interview costs you the question
- Believing the most specific container always wins
- Using <Directory> to protect a proxied URL path
- Assuming a later <Location> cannot override an earlier <Directory>
- Treating <Location> as a reliable guard for files on disk
- Thinking configtest passing means a restriction actually applies