Repository navigation
Front Door Per App Split
Reference page for how Azure Front Door routes app traffic, repo digitalspace/eagle-edge.
See Azure Hosting for the profile-wide setup.
Front Door holds one origin group per app on both estates and matches on UrlPath. Test and prod
share the same routes and patterns; only the origin hostname differs:
| Origin group | Route(s) | Pattern | Test origin | Prod origin | Probe |
|---|---|---|---|---|---|
og-eagle-public |
default |
/* |
Storage static website | Storage static website | — |
og-eagle-api |
eagle-api |
/api, /api/*, /eguide, /eguide/*
|
eagle-api-6cdc9e-test.apps.silver.devops.gov.bc.ca |
eagle-api-6cdc9e-prod.apps.silver.devops.gov.bc.ca (Route eagle-api-direct) |
GET /api/health |
og-eagle-admin |
admin |
/admin, /admin/*
|
eagle-admin-6cdc9e-test.apps.silver.devops.gov.bc.ca, unused; the OpenShift objects are retired |
eagle-admin-6cdc9e-prod.apps.silver.devops.gov.bc.ca (Route eagle-admin-direct), unused while adminOrigin = static; kept for rollback only |
HEAD /admin/ |
og-eagle-admin-static |
admin |
/admin, /admin/*
|
Storage static website eagleadmtestvymaysch2agd
|
Storage static website eagleadmprodrhj3zycszo6j
|
— |
og-demi |
demi, demi-cached, demi-documents
|
/demi-search/*, /demi-projects/*, /s/*, /demi/api/* (demi-cached: /demi-search/documents/recent-uploads only, cached, query string kept; demi-documents: /demi-search/documents/*, cached per query string) |
demi-apim-test.azure-api.net |
demi-apim-prod.azure-api.net |
none — APIM Consumption bills per call |
og-analytics |
usage, analytics
|
/api/usage/*, /api/analytics, /analytics, /analytics/*
|
demi-apim-test.azure-api.net |
demi-apim-prod.azure-api.net |
— |
On /demi/api/* the demi rule set allows only GET, HEAD and OPTIONS, and only the read prefixes projects, documents, users, groups, inspections, lists, organizations, comment-periods, comments, updates, notifications, counts and me under /demi/api/; other requests go to /edge-blocked (azure/modules/front-door.bicep, eagle-edge).
Front Door rule sets carry the path rewrites for these routes: rulesdemi rewrites
/demi-search/search to /api/search, /demi-projects/<id> to /api/projects/<id>, and /s/ to
/api/s/; rulesusage rewrites /api/usage/ to /analytics/ and /api/analytics to /analytics;
ruleseagleapi carries the /api/v2/projects to /api/public/project alias. rulesadmin
holds the adminslash redirect rule and ruleseagleapi also holds the ssdfactsheet
redirect rule, both carried over from the retired rulesbackend set (source:
eagle-edge/azure/modules/front-door.bicep lines ~956-959).
The admin route has two origin groups, og-eagle-admin (OpenShift Route, retired on test, kept for rollback on prod) and
og-eagle-admin-static (storage web endpoint). Bicep param adminOrigin in
azure/main.<env>.bicepparam picks one:
| Env | adminOrigin |
Serves /admin/ from |
|---|---|---|
| Test | static |
Storage static website eagleadmtestvymaysch2agd, resource group c4b0a8-test-rg, profile eagle-edge-test
|
| Prod | static |
Storage static website eagleadmprodrhj3zycszo6j, resource group rg-eagle-public-prod, profile eagle-edge-prod, endpoint eagle-public-prod
|
Dev has no admin app: the OpenShift eagle-admin objects in 6cdc9e-dev and 6cdc9e-test are
retired (Helm release uninstalled, Routes eagle-admin and eagle-admin-direct deleted) and there
is no Azure dev edge. Prod keeps Deployment eagle-admin (0 replicas), its Service, Route
eagle-admin-direct and ConfigMap eagle-admin-env-js in 6cdc9e-prod, and the
6cdc9e-tools image streams and build configs, for rollback only
(Rollback Procedures).
deployAdminSite creates the storage account, og-eagle-admin-static and the extra rulesadmin
rules. adminUploaderPrincipalId is the object id of the CI identity eagle-admin-cicd-<env>;
it grants that identity its roles on the account and the profile.
Rule set rulesadmin:
| Rule | Effect |
|---|---|
adminslash |
301 /admin to /admin/
|
spafallback |
Rewrites to /admin/index.html when the last path segment has no dot and the path is not under /admin/api
|
spafallbackmatrix |
Same rewrite when the last segment has matrix params (;) |
securityheaders |
HSTS, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy, and a Content-Security-Policy header, enforced on test and prod. connect-src allows the Keycloak host and https://*.in.applicationinsights.azure.com
|
A missing asset returns 404. /admin/api/* is never rewritten. /api/* goes to the eagle-api
route on the same host; eagle-api runs on OpenShift.
Front Door drops a leading slash from request-path condition values, so a RegEx value must not
start with /.
A route switch between the two origin groups takes about 20 minutes to reach every edge location. The origins carry different builds, so during that window users can get 404 for hashed assets.
Rollback for test and prod: Rollback Procedures. Full parameter notes: "eagle-admin
static origin" in the eagle-edge README.
The origins refuse requests that skip Front Door. Both gates ship off on prod until the rollout; test has them on (eagle-api) or in log mode (DEMI).
| App | Gate | How it decides | Exempt |
|---|---|---|---|
| eagle-api | Middleware api/middleware/edgeOnly.js
|
For requests to the direct Route host, 403 unless X-Azure-FDID equals FRONT_DOOR_ID
|
/api/health; other hosts (in-cluster callers, the console Route) |
| DEMI |
dispatch in src/http/router.js, setting EDGE_GATE
|
403 in enforce mode unless the request came through APIM and carries the edge secret, X-APIM-Subscription or X-Api-Key
|
/health* |
eagle-api turns the gate on with Helm value directRoute.edgeOnly; see Configuration Management. DEMI modes and the log query are in the eagle-demi wiki page Azure-Environments.
The eagle-api gate has two modes, set by EDGE_ONLY_MODE. enforce answers 403. log serves the request and records it. Both modes record every refusal as the App Insights custom event edge-gate-refusal, with properties path, host and mode, whatever LOG_LEVEL is. Count them:
AppEvents
| where Name == "edge-gate-refusal"
| summarize n=count() by path=tostring(Properties.path), mode=tostring(Properties.mode)
| order by n descProd rollout: turn the gate on in log mode, read the query for a few days, then switch to enforce.
The og-eagle-api probe asks /api/health, which the gate exempts and which does not touch the database.
Rules in ruleseagleapi send GET and HEAD requests for the old eagle-api document paths to
/demi-search/documents/<id>/download?inline=1. redirect=1 is not sent, so DEMI answers with the
file itself instead of a second 302 to the object store. The address bar stays on the site host.
Paths and rule names are in Eagle-Search.
Route demi-documents (/demi-search/documents/*, origin group og-demi, rule set rulesdemi)
caches those answers. The cache key is the path plus the query string, compression is off, and
/demi-search/documents/recent-uploads still matches demi-cached first. DEMI sets
public, max-age=300 on a public file and no-store or private on everything else, so only the
file is cached, for 5 minutes. Front Door revalidates with Last-Modified; it ignores ETag.
Rule demidocumentinline in rulesdemi (order 12) catches links that still carry
redirect=1&inline=1, which eagle-public's Angular line builds. It matches a 24-hex id and a query
string holding both parameters, in any order, and answers 302 to the same path with ?inline=1
alone. A redirect=1 link without inline=1, and a UUID id, are not touched.
Deploy the edge after DEMI. Until DEMI runs the stream mode, ?inline=1 alone returns JSON, not the
file. The route and rules are on test and prod (eagle-edge v1.4.6 and later). On prod, DEMI v0.172.2 and later runs stream mode. The APIM policy buffer-response="false" is live on test and prod. After a deploy, Front Door edges converge unevenly for 7 to 10 minutes: some edges apply the new route's rules while demi still strips only /demi-search, and every document download answers 404. Deploy at low traffic and re-check downloads 10 minutes after the deploy job finishes. DEMI side:
ADR-011-Public-Read-Surface and Operating-DEMI-in-Azure.
Rule blockscraperua is in ruleseagleapi (order 8), rulesdemi (order 4) and rulesdemidocs (order 2). A request whose User-Agent contains document research, propertypdfresearch or rldata-ingest (any case) gets a 302 to /robots.txt and the rule set stops, so no origin sees it. Rules attach to routes, so *.azurefd.net hostnames are covered.
Deploys are Incremental, so removing the rule from the template leaves it on the profile. Remove it per rule set:
az afd rule delete -g <rg> --profile-name eagle-edge-<env> --rule-set-name ruleseagleapi -n blockscraperua
az afd rule delete -g <rg> --profile-name eagle-edge-<env> --rule-set-name rulesdemi -n blockscraperua
az afd rule delete -g <rg> --profile-name eagle-edge-<env> --rule-set-name rulesdemidocs -n blockscraperuaSecurity policy sp-<env> binds the WAF policy to the custom domains and to the endpoints, so *.azurefd.net hostnames are inspected too. The WAF policy itself is managed outside the template.
eagle-public ships public/robots.txt on both release lines. It sits at the $web root and Front Door serves it as a file. It allows all crawlers except under /analytics and /admin, and carries a Sitemap: line.
GET /api/public/sitemap.xml on eagle-api lists projects only, as <PUBLIC_SITE_URL>/p/<project id>. The response is cached for one hour (Cache-Control: public, max-age=3600).
The rproxy Deployment in 6cdc9e-test is scaled to 0 replicas. eagle-test.apps.silver.devops.gov.bc.ca
(the OpenShift route that fronted test app traffic) returns 503; nothing routes app traffic to it.
The rproxy Deployment in 6cdc9e-prod runs 0 replicas. It is scaled to 0 directly on the cluster,
outside Helm. The deployed Helm release still has replicaCount: 2 (release revision 30, 2026-09-10).
replicaCount: 0 is in helm/rproxy/values-prod.yaml only on the eao-nginx master branch. That change
is newer than the highest tag, v2.7.40. Deploy to Prod deploys a tag, so the change has not reached prod.
A helm rollback, an upgrade that reuses the release values, or a Deploy to Prod run with tag v2.7.40
or older sets rproxy back to 2 replicas. Do not run any of these for rproxy until a tag that contains
replicaCount: 0 exists.
Front Door routes projects.eao.gov.bc.ca and www.projects.eao.gov.bc.ca straight to the per-app origins above.
Rollback:
- Test:
oc scale deploy rproxy -n 6cdc9e-test --replicas=2. - Prod:
oc scale deploy rproxy -n 6cdc9e-prod --replicas=2, then repoint Front Door's custom domains at the OpenShift route again.
Front Door's routes are not reverted by scaling rproxy back up; rolling back the edge itself needs
the per-app origin params redeployed empty so the estate falls back to og-eagle-backend /
api-passthrough, or a redeploy of the pre-split tag.
Eagle Documentation
Operations
- Deployment Pipeline
- Helm Charts
- Reverse Proxy
- Rollback Procedures
- Eagle Public Release Lines
- Eagle Search Archive
- Azure Migration Strategy
- Azure Hosting
- Front Door Per-App Split
- Front Door Ban Ladder
- Error Logging
- Secrets Management
Architecture
- Architecture Overview
- API Architecture
- Analytics Architecture
- Angular Patterns
- Eagle Public Page Layout
- Eagle Public Adding A Page
- Extended Project Pages
- ENGAGE–EAGLE Integration
- BCGW Project Export
- Eagle Search
- Document Content Search
- Eagle DEMI
- Eagle DEMI Push Sweep
- Eagle DEMI Re-Push and Backfill
- Eagle Notify
- Project Data Models
- Async Jobs API
- Bulk Document Download
Configuration
Help
Repositories
Environments