SEO Readiness Portal Cloud Migration — 2026-06-01 SEO Readiness Portal Cloud Migration — 2026-06-01 Date: 2026-06-01 Owner: john Verdict: DONE Live state verified (2026-06-01 05:31 UTC) curl -sI https://seo-tools.alai.no/api/health → HTTP 302 to alai-no.cloudflareaccess.com/cdn-cgi/access/login/... (CF Access enforced, no anonymous access) curl -sI https://seo-readiness-alai.azurewebsites.net/api/health → HTTP 403 x-ms-forbidden-ip: 46.46.245.169 (Azure origin lock to Cloudflare IPs) No traffic to John local host: cloudflared route for seo-tools.alai.no and seo-tools.snowit.ba set to http_status:503 . Architecture (now) Browser → Cloudflare Access ( seo-tools.alai.no , alai-no team) → Cloudflare proxied DNS → Azure App Service Linux container seo-readiness-alai (Sweden Central, rg rg-seo-readiness-prod , asp asp-seo-readiness-prod B1) → Next.js standalone in alairegistry.azurecr.io/seo-readiness-portal:20260531-cloud (digest sha256:16c8a40a... ) → persistent /home/data/workspace.json App access mode: SEO_PORTAL_ACCESS_MODE=cf-access , trusted header CF-Access-Authenticated-User-Email , allowed domains snowit.ba,alai.no , extra allowed alembasic@gmail.com . Evidence Deploy summary: /tmp/alai/seo-readiness-cloud-migration-20260531/cloud-deploy-summary.md Local-disabled proof: /tmp/alai/seo-readiness-cloud-migration-20260531/local-disabled-evidence.txt Azure build + deploy: azure-build.log , azure-webapp-deploy.log , azure-hostname-corrected.log , azure-role-settings.log Origin lock: azure-access-restrict-cloudflare.log , azure-access-restrict-smoke.log DNS upsert: cf-dns-upsert.log Public smoke: public-cloud-smoke.log Origin smoke: azure-smoke.log Authenticated UAT (alem@alai.no, end-to-end: partners → add client → intake → audit → report → markdown export): uat-alem/seo-cloud-uat-result.json + 8 PNG screenshots uat-alem/01-cf-login.png … 08-export.png Lesson memo: lesson-seo-cloud-origin-lock-20260531.md Docs corrected (no localhost as final target) DEPLOYMENT-CLOUD.md L7 — explicit "must not be served from John/local localhost" DEPLOYMENT-INTERNAL.md L7, L26, L102 — Docker steps marked local-dev only; cloud origin required for production; explicit "Do not route seo-tools.* to http://127.0.0.1:* or any localhost address" README.md L130 — "Azure App Service Linux container origin, not John/local localhost" BUILD-BLUEPRINT.md L54, L70 — Azure runbook section + CEO correction recorded Rollback If Azure origin needs rollback: Revert Web App container image: az webapp config container set -g rg-seo-readiness-prod -n seo-readiness-alai --container-image-name alairegistry.azurecr.io/seo-readiness-portal: CF Access policy and DNS remain unchanged; no public bypass introduced. Do NOT re-enable cloudflared local route — that violates the CEO correction. Open follow-ups (separate MCs, not blockers) Postgres swap for /home/data/workspace.json (durable multi-user storage) — pre-req for external product use. seo-tools.snowit.ba DNS/zone provisioning (currently 503). Bind Azure custom hostname TLS managed cert (currently CF terminates). CEO scope check "Move off John/local host to real cloud" → DONE (Azure App Service) "Cloudflare Access trusted-header preserved" → DONE ( SEO_PORTAL_ACCESS_MODE=cf-access ) "No public unauthenticated access" → DONE (302 to CF Access login on unauth) "Custom domain toward seo-tools.alai.no" → DONE (CNAME + asuid TXT + Azure binding) "Concrete deploy URL/origin" → DONE ( seo-readiness-alai.azurewebsites.net behind seo-tools.alai.no ) "UAT evidence" → DONE (alem-authenticated screen-recorded flow) "Rollback" → documented above "Fix docs that recommend local host" → DONE (4 docs updated) MC / evidence references MC task: #102653 Local closure artifact: /tmp/alai/seo-readiness-cloud-migration-20260531/CLOSURE-102653.md Cloud deploy summary: /tmp/alai/seo-readiness-cloud-migration-20260531/cloud-deploy-summary.md Authenticated UAT result: /tmp/alai/seo-readiness-cloud-migration-20260531/uat-alem/seo-cloud-uat-result.json Origin restriction smoke: /tmp/alai/seo-readiness-cloud-migration-20260531/azure-access-restrict-smoke.log P2P verifier PASS: mesh-thr-50503688-7de8-489c-96ca-3d7d1a165dc2 See Also — Agent Runbook The canonical end-to-end workflow runbook (intake to John deep-report, anti-pitfalls, trigger checklist): SEO Pipeline — Portal Intake to John Deep-Report (agent runbook)