skip to content

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?

level: seniorimportance: should knowfreq 46%

answer

  1. different keys: path, filename, URL
  2. URL rules are merged last
  3. later section wins the conflict
  4. a proxied request has no file
  5. 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 s

The 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
apacheconf
# 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

for a junior

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.

for a middle

Reproduce the merge sequence — Directory, DirectoryMatch, Files, Location, If — and explain that later stages override earlier ones per directive rather than replacing whole blocks.

for a senior

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.

for a principal

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

context