Custom rewrites & location blocks
BOA has no .htaccess layer — every per-site rewrite, redirect, or custom
location block is delivered through an operator include file dropped
into a fixed directory and pulled into the rendered vhost at a defined
position. The include file is never touched by the Provision backend, so it
survives every barracuda upgrade and every Verify.
The two include points
The runtime vhost (Inc/vhost_include.tpl.php) globs two operator filenames
from the per-frontend post.d directory and includes each at a different
depth:
| Filename | Included at | Override depth |
|---|---|---|
nginx_force_include.conf |
early in the vhost, before BOA's standard location set (vhost_include.tpl.php:323) |
high — can override cache-bypass and static-file handling |
nginx_vhost_include.conf |
mid-body — after the early cache-bypass and static-file locations but before the private-download, CSS/JS, and PHP-handler locations (vhost_include.tpl.php:1025) |
standard — overrides rules "below" it (private-download, CSS/JS, the @cache/@drupal/PHP handlers), not the higher-level cache-bypass/static rules above it |
Both are globbed with a trailing *, so a nginx_force_include.conf.foo
suffix variant is also picked up. A sibling fpm_include* glob
(vhost_include.tpl.php:328) is pulled in alongside nginx_force_include
for per-site PHP-FPM version overrides.
Choose nginx_vhost_include.conf first; switch to nginx_force_include.conf
only when the override does not take because a higher-priority BOA block is
winning the match.
Drop directories
The include is read from $aegir_root/config/server_master/nginx/post.d/,
where $aegir_root resolves per frontend:
| Frontend | post.d drop directory |
|---|---|
| Octopus Satellite (per instance) | /data/disk/<USER>/config/server_master/nginx/post.d/ |
| Master (host-level Aegir frontend) | /var/aegir/config/server_master/nginx/post.d/ |
Both rows carry the server_master/nginx/post.d/ suffix — that is the only
path the rendered vhost's include directive reads. A file dropped into
/var/aegir/config/includes/ (a real directory that holds the
ip_access/ generated includes) is never picked up as a custom rewrite;
that directory is not on the post.d include path.
Drop the file, then re-emit the vhost (below). The file's content is copied by reference at include time, not merged into the vhost, so your edits persist verbatim across upgrades.
Scope of custom rules
A custom include applies to every site on the same frontend unless each
block is scoped to a host. The canonical pattern wraps each interesting
block in an if ($host ~* …) { … } or a server_name guard so it only
fires for one domain. Without that guard the rule leaks to every vhost the
include is pulled into.
Recipes
Map legacy content paths to multisite
location ~* ^.+\.(?:jpe?g|gif|png|ico|swf|pdf|ttf|html?)$ {
access_log off;
log_not_found off;
expires 30d;
rewrite ^/files/(.*)$ /sites/$server_name/files/$1 last;
rewrite ^/images/(.*)$ /sites/$server_name/files/images/$1 last;
rewrite ^/downloads/(.*)$ /sites/$server_name/files/downloads/$1 last;
rewrite ^/download/(.*)$ /sites/$server_name/files/download/$1 last;
rewrite ^/docs/(.*)$ /sites/$server_name/files/docs/$1 last;
rewrite ^/documents/(.*)$ /sites/$server_name/files/documents/$1 last;
rewrite ^/legacy/(.*)$ /sites/$server_name/files/legacy/$1 last;
try_files $uri =404;
}
For migrations where legacy URLs (/files/foo.pdf) must land under Drupal's
multisite-aware /sites/<domain>/files/.
Site-specific 301 with a parent literal location
The parent literal location ^~ /path stops Nginx from searching other
regex-based locations, so the nested regex wins over BOA's own regex blocks:
location ^~ /some-literal-path/no-regex-here {
location ~* ^/some-path/or-regex-here {
if ($host ~* ^(www\.)?(domain\.com)$) {
return 301 $scheme://$host/destination/url;
}
try_files $uri @cache;
}
}
Without the parent literal block, the regex location loses to BOA's regex blocks.
Bulk 301 for legacy .php URIs
location ^~ /services {
location ~* ^/services {
rewrite ^/services/accounting\.php$ $scheme://$host/node/18 permanent;
rewrite ^/services/assurance\.php$ $scheme://$host/node/11 permanent;
rewrite ^/services/audit\.php$ $scheme://$host/node/11 permanent;
rewrite ^/services/taxation\.php$ $scheme://$host/node/92 permanent;
rewrite ^/services/wealth\.php$ $scheme://$host/node/15 permanent;
rewrite ^/services\.php$ $scheme://$host/node/17 permanent;
try_files $uri @cache;
}
try_files $uri @cache;
}
For sites migrated off Joomla / Drupal 6 / legacy WordPress that exposed
.php URIs. Note that bare .php requests are also subject to the
abuse-guard .php catch-all (see Edge policy); a
matched literal location short-circuits before that guard.
Domain-specific single-URL redirect
location = /about_us.php {
if ($host ~* ^(www\.)?(foo\.com)$) {
return 301 $scheme://$host/node/19;
}
return 444;
}
location = /… is the most specific match — it short-circuits location
matching entirely. return 444 (Nginx's "close without response") quietly
drops the request for other hosts that hit the same URI.
Avoid 404s on sites/default/files/ legacy paths
For a site moved from sites/default/ to sites/<domain>/:
if ($main_site_name = '') {
set $main_site_name "$server_name";
}
location ^~ /sites/default/files {
location ~* ^/sites/default/files/imagecache {
access_log off;
log_not_found off;
expires 30d;
set $nocache_details "Skip";
rewrite ^/sites/default/files/imagecache/(.*)$ /sites/$main_site_name/files/imagecache/$1 last;
try_files $uri @drupal;
}
location ~* ^/sites/default/files/styles {
access_log off;
log_not_found off;
expires 30d;
set $nocache_details "Skip";
rewrite ^/sites/default/files/styles/(.*)$ /sites/$main_site_name/files/styles/$1 last;
try_files $uri @drupal;
}
location ~* ^/sites/default/files {
access_log off;
log_not_found off;
expires 30d;
rewrite ^/sites/default/files/(.*)$ /sites/$main_site_name/files/$1 last;
try_files $uri =404;
}
}
How the include interacts with the rendered vhost
Provision renders the vhost in two parts: the per-site vhost.tpl.php
frames the server and SSL blocks, while Inc/vhost_include.tpl.php is
rendered once per frontend as the shared nginx_vhost_common.conf
(under config/includes/, pulled into every vhost) carrying the
Drupal-specific locations, the cache and static-file handling, and the
PHP-FPM upstream. The two include points land your file at one of the two
well-defined positions inside that shared body — force_include before any
location block at all, vhost_include mid-body: after the early
cache-bypass and static-file locations but ahead of the private-download,
CSS/JS, and PHP (@cache/@drupal/.php$) handlers.
Applying the change
A custom include takes effect only after the vhost is re-emitted. Re-emission runs from a Verify on the affected site (per-site change) or on the platform (change that applies to every site on the platform):
# As the Aegir user, via Drush:
drush @<site-alias> provision-verify
Verify rewrites the vhost to
/var/aegir/config/server_master/nginx/vhost.d/<site> (Master) or the
/data/disk/<USER>/…/vhost.d/<site> equivalent on an Octopus instance,
re-pulls the post.d includes, runs configtest, and reloads Nginx.
Debugging an include that does not take
- Read the rendered vhost on disk — confirm the
includedirective in the emittedvhost.d/<site>references yourpost.dfile. - Check syntax —
nginx -treports errors from include files with the include path and line. - Reload after a config-only edit —
service nginx reload. (Verify auto-reloads; a manual edit of the include does not until the next reload.) - Rule out the cache — the Speed Booster cache may be serving the old
response. Bypass with a
.dev.host or wait for the cache TTL. - Wrong include point — if a higher-priority BOA block is winning, move
the rule from
nginx_vhost_include.conftonginx_force_include.conf.
Related
- Config templates — the BOA-deployed templates
and the Provision master
http{}config your includes layer over. - Nginx debugging — diagnosing 502/504 and the redirect-to-install loop.
- Edge policy — the guard chain (
.phpcatch-all, secret-path deny) a literal-locationinclude short-circuits past. - Reference appendix — consolidated path and variable tables.