skip to content

You own an Apache fleet where every application ships its own .htaccess file. How would you decide whether to keep AllowOverride enabled or move those rules into the server configuration?

level: principalimportance: should knowfreq 33%

answer

  1. it is a delegation question first
  2. who can reload the server
  3. parsed once versus every request
  4. configtest never sees .htaccess
  5. grant a group, not All

basics

~20 s

Decide on who needs to change rules without a server reload. Where teams control the server config, hoist rules into <Directory> blocks and set AllowOverride None: config is parsed once, reviewable and syntax-checked. Keep .htaccess only where delegation to untrusted or reload-less tenants is the actual requirement.

solid answer

~60 s

The question is not really performance, it is authority. `.htaccess` exists so somebody who cannot edit the server config and cannot reload the server can still change behaviour for their subtree. If that constraint is real — shared hosting, tenants you do not control, an application whose deploy artifact legitimately carries its routing — keep it, and narrow it with `AllowOverride FileInfo` rather than `All`, or `AllowOverrideList` for named directives. If the constraint is not real, which it usually is not on a fleet you own, hoisting the rules into `<Directory>` blocks buys three things: they are parsed once instead of per request, they are visible to code review and to `apachectl configtest` before they go live, and a mistake fails a reload rather than becoming a live 500. I would migrate tree by tree, copying rules in with per-directory path handling adjusted, running both for a release, then flipping to `AllowOverride None` and measuring — and I would keep an explicit exception list rather than pretending no tenant needs it.

code

apache · 16 lines
apache
# Platform-owned tree: rules live here, parsed once, checked by configtest.
<Directory "/var/www/app">
    AllowOverride None
    Require all granted

    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^ /index.php [L]
</Directory>

# Named exception: tenants may set redirects and headers, nothing else.
<Directory "/srv/tenants">
    AllowOverride FileInfo
    Require all granted
</Directory>

go deeper

for a junior

Know that the same rewrite rules can live either in an .htaccess file or in the server configuration, and that AllowOverride decides whether the .htaccess version is honoured at all.

for a middle

Be able to list the concrete differences — parsed once versus per request, checked by configtest versus never checked — and to name the override groups instead of reaching for All.

for a senior

Show a migration that can be attributed: copy first, run both, flip per tree while watching that tree's error rates, and gate config deploys on a syntax check before inverting the blast radius.

for a principal

Own it as a delegation and governance decision — who may change traffic behaviour without a reload, what the named exceptions are, and what build-time enforcement keeps the policy alive after you stop watching.

## Frame it as a delegation decision Every discussion of `.htaccess` starts with the per-request cost, and that is the least interesting part of the answer. The cost is real — a directory walk and a re-parse on every hit — but on most sites it is small and it is not what makes the choice hard. What makes it hard is that `.htaccess` moves configuration authority from the people who run the server to the people who deploy content. Decide that question first, and the rest follows. Ask: **who must be able to change a rewrite rule, and what are they allowed to touch?** - If the answer is "a tenant who has FTP access to their document root and no way to reload httpd", `.htaccess` is not a wart, it is the feature, and removing it removes the product. - If the answer is "our own application teams, who ship through a pipeline that could just as easily ship a config fragment and trigger a reload", then the delegation is accidental. It was inherited from a tutorial, not chosen. Most fleets are the second case and behave as though they were the first. ## What you gain by hoisting Moving rules into `<Directory>` blocks in the server configuration changes four properties: **Parsed once.** Server config is read at startup; `.htaccess` is re-read and re-parsed on every request that touches the tree, and with `AllowOverride None` the server does not even look for the files. **Reviewable.** Rules in config are in the same repository, the same review flow and the same audit trail as the rest of the platform. Rules in `.htaccess` are invisible to anyone reading the server config — a genuinely dangerous property when the file contains access-control or redirect logic that someone later has to reason about during an incident. **Syntax-checked before it ships.** `apachectl configtest` validates server config. It does not validate `.htaccess`, because those files are not read until a request arrives. A typo in server config fails a reload, which your deploy catches; the same typo in `.htaccess` is a live 500 for every request under that directory until someone notices. **Debuggable in one place.** Cross-directory rewrite interactions — a rule in one tree feeding a rule in another — are readable when both are in the config and nearly invisible when they are scattered across content directories. ## What you give up Be honest about the other side, because the argument is not one-sided: - **Reload coupling.** A rule change now needs a config deploy and a graceful reload. On a fleet with a config management pipeline that is a non-event; without one, you have just made small changes expensive and people will route around you. - **Blast radius inverts.** A broken `.htaccess` breaks one directory tree. A broken server config can fail the reload for a whole server — better, because it fails *before* traffic, but only if your pipeline actually gates on `configtest`. If it does not, hoisting makes things worse, so fix that first. - **Portability.** An application whose rewrite rules genuinely travel with the code — a packaged CMS or framework installed by users — expects to carry its own `.htaccess`. Stripping it means owning a config fragment per application version, forever. ## Use the middle settings This is not binary, and the granularity is the part most teams skip. `AllowOverride All` is almost never the right answer even when overrides are wanted. Grant the group that is actually needed — `FileInfo` for rewriting and redirects, `AuthConfig` for authentication — and use `AllowOverrideList` (httpd 2.3.11 and later) when you want to permit two named directives rather than a whole group. You can scope this per tree: `None` on the platform's own document roots, `FileInfo` on the tenant trees. The security dimension follows the same shape. Anything a tenant can write into `.htaccess` is configuration you are executing on their behalf; the override groups are precisely the mechanism for bounding that. ## How I would actually run the migration 1. **Inventory.** Find every `.htaccess` under every document root and classify it: rewriting, auth, headers, directory indexing, or dead. A surprising share are dead — copied in years ago, matching nothing. 2. **Decide the exception list first.** Name the trees that keep overrides and why. A policy with no exceptions gets ignored the first time it is inconvenient. 3. **Copy, do not move.** Transcribe rules into `<Directory>` blocks, adjusting per-directory path handling, and run both for a release. Because rules in server config for a `<Directory>` are still per-directory context, most rules transfer unchanged; the ones that do not are usually relying on a derived rewrite base. 4. **Flip to None per tree**, watching 404 and 500 rates for that tree specifically. Flipping globally in one change makes attribution impossible. 5. **Measure, and be willing to report that it did not matter.** If request rates are modest, the latency win will be in the noise; the review and safety wins are the ones worth claiming. Overstating the performance case is how the whole initiative loses credibility. 6. **Prevent regression.** A deploy-time check that fails a build introducing a new `.htaccess` outside the exception list is what makes the decision stick a year later. ## The judgment to voice A good answer refuses the absolute. "Always disable `.htaccess`" is a benchmark result applied to an organisational question, and "leave it, it's fine" ignores that unreviewed, untested configuration is executing on every request. The defensible position is: default to server config with `AllowOverride None`, keep a named and justified exception list for genuine delegation, never grant `All` where a group will do, and gate config deploys on a syntax check so that the blast-radius inversion works in your favour rather than against it.

  • A team argues that hoisting rules into server config is riskier because one bad line can take down the whole server. How do you answer?
    It is a real inversion of blast radius, and it argues for gating the pipeline, not for keeping .htaccess. A syntax error in server config fails `apachectl configtest` and the graceful reload, so no traffic is affected — provided your deploy runs configtest and refuses to reload on failure. The same error in .htaccess is never checked and becomes a live 500 on the next request. Fix the gate, then hoist.
  • How would you quantify the performance benefit before committing to the change?
    Measure rather than cite the manual. Compare a tree with AllowOverride None against the same tree with overrides enabled, at your real request rate and real path depth, and look at CPU and tail latency for cheap static requests where the fixed overhead dominates. On a modest site the delta will be in the noise, and the honest report is that the win is review and safety rather than speed — overstating latency gains is how the initiative loses credibility.
  • Which trees would you never take .htaccess away from?
    Any tree whose tenants cannot reload httpd and cannot ship config — shared hosting is the archetype — and packaged applications that ship their own rewrite rules with each release, where owning a config fragment per version is worse than the per-request cost. In both cases I would still narrow the grant to the group actually needed, typically FileInfo, rather than leaving AllowOverride All in place.
  • How do you stop new .htaccess files reappearing a year later?
    Make it a build-time check, not a policy document: fail the deploy when an artifact contains an .htaccess outside the named exception list. Pair it with a periodic scan of the document roots, since files arrive by routes other than the pipeline. Decisions that depend on people remembering a wiki page erode within one team rotation; decisions enforced by a red build survive it.

saying these in an interview costs you the question

  • Says .htaccess is free because the file is tiny
  • Assumes disabling overrides breaks all rewriting
  • Grants AllowOverride All when FileInfo would do
  • Treats a server-config syntax error as harmless
  • Frames the choice purely as a benchmark result

context