How bwscomputerservice.com moved from manual single-file uploads to a
Git-connected auto-deploy pipeline, and got a branded remote-support page
added in the process. Done 2026-07-26. No downtime - the live homepage
never dropped during the migration.
The payoff: the site now works exactly like the Hugo KB - edit in VS Code, push to GitHub, Cloudflare auto-deploys. Adding a page is now just adding a file and pushing.
| Piece | What it is |
|---|---|
| Hosting | Cloudflare Worker with static assets (bws-main), NOT Cloudflare Pages |
| Source repo | github.com/NeuralNetworkTrader/bws-site (private) |
| Deploy trigger | Git push to main -> Cloudflare Workers Builds -> auto-deploy |
| Site files | live in public/ in the repo |
| Config | wrangler.jsonc in repo root tells Cloudflare to serve public/ |
| Remote support | Zoho Assist branded portal, linked (not embedded) from /support |
bws-site/
├── wrangler.jsonc # tells Cloudflare: serve ./public as static assets
├── .gitignore
└── public/
├── index.html # homepage
├── support.html # remote-support page (served at /support)
└── _headers # security headers (CSP, X-Frame-Options, etc.)
wrangler.jsonc contents:
{
"name": "bws-main",
"compatibility_date": "2026-07-01",
"assets": {
"directory": "./public",
"not_found_handling": "404-page",
"html_handling": "auto-trailing-slash"
}
}
The "name" MUST match the existing Worker (bws-main) exactly, or a Git deploy
creates a duplicate Worker instead of updating the real one.
cd ~/Projects/bws-site
# edit or add a file in public/ (e.g. public/pricing.html)
git add .
git status # confirm the file is staged
git commit -m "Add pricing page"
git push -u origin main # triggers Cloudflare build + deploy
Cloudflare builds and deploys automatically on push. A new file in public/
is live at its path (e.g. public/pricing.html -> /pricing) within a minute
or two. Wait for the build to finish before checking (see cache gotcha below).
This tripped us up during the migration. The A+ header grade is produced by two separate mechanisms, and both must stay in place:
_headers file (in public/) provides:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;X-Frame-Options: SAMEORIGINReferrer-Policy and Permissions-PolicyStrict-Transport-Security (HSTS) - this is NOT in the _headers file;
it’s set at the zone level (SSL/TLS -> Edge Certificates -> HSTS).So: the _headers file is version-controlled and deploys with the site, but
HSTS lives in the Cloudflare dashboard. Don’t go looking for HSTS in the repo -
it’s not there, and that’s correct. If the A+ grade ever drops, check BOTH
sources.
Verify headers after any deploy:
curl -I https://bwscomputerservice.com/support
Want to see: content-security-policy, permissions-policy, referrer-policy,
x-frame-options, strict-transport-security, x-content-type-options.
Cross-check with https://securityheaders.com (should show A+).
A branded remote-support landing page. Design goal for a repair business: make a nervous, non-technical person feel safe letting someone control their computer (the exact scenario scam-callers exploit). So the page leads with trust: plain-language explanation, a clear 3-step flow, a “you’re in control” note, and an explicit scam warning (“we never cold-call asking for access”).
https://assist.zoho.com/portal/bwscomputerservice) - opens in a new tab.frame-src), which would drop the security posture and trip
the domain monitor. A plain outbound link needs zero CSP changes. Keep it a link./support (no .html) - the html_handling setting redirects
/support.html -> /support with a 307. Share the clean /support form.Remote support platform choice: Zoho Assist Standard (~$10/tech/mo billed annually, 2 concurrent, reboot-and-reconnect, branded portal domain). Chosen over TeamViewer ($24.90/mo) on cost + branded-domain feature. One-time attended fixes only - nothing persists on the client after the session. (For ongoing managed clients, the plan is a Wazuh agent instead - different tool, different job.)
These all bit us during the build. Documented so future-me doesn’t re-learn them.
Worker vs Pages - stayed on Worker. The site was already a Worker with static assets (Cloudflare’s recommended architecture for 2026). Workers now support Git integration too (Workers Builds), so there was NO need to migrate to a Pages project. Connecting the existing Worker to Git = far less risk than a platform migration. One-way door warning: once a Worker/Pages project uses Git deploy, you can’t switch back to manual Direct Upload.
.gitignore copied from the Hugo repo ignored /public/. In Hugo, public/
is generated build output you ignore. Here, public/ is the ACTUAL SITE SOURCE.
The copied .gitignore would have pushed an empty repo and deployed a blank site.
Fix: removed the /public/ and /resources/ lines. Lesson: config files
copied between projects need their project-specific assumptions checked -
/public/ means opposite things in Hugo vs a raw static site.
Always git status before committing to confirm the real files are staged.
SSH push failed - this box uses gh (GitHub CLI), not an SSH key.
git@github.com: Permission denied (publickey) happened because there’s no SSH
key configured; auth goes through the gh CLI (OAuth token from gh auth login).
Fix: use the HTTPS remote (https://github.com/.../bws-site.git) which works
with the cached gh credentials, or use gh repo create ... --source=. --push.
Check auth with gh auth status.
First Cloudflare connect needed wrangler.jsonc first. The Git connection’s
deploy command is npx wrangler deploy, which needs a wrangler config in the
repo to know what/how to deploy. The repo didn’t have one initially. Fix: added
wrangler.jsonc (above) declaring public/ as the assets dir, committed+pushed,
THEN connected. Without it, wrangler deploy has nothing to read.
404 right after deploy = cache/timing, not a real error. Checked /support
seconds after pushing and got a 404. The build hadn’t finished propagating and the
browser/edge cached the 404. Fix: wait a minute, hard-refresh (Ctrl+Shift+R) or
use incognito. It was fine once the build completed. Don’t panic-debug a 404 in
the first minute after a push.
The reused API token is named “cyber-kb build token”. Cosmetic - the Workers Build uses a token originally made for the KB repo. Works fine, just an odd name to see attached to the website Worker. Not worth changing.
Git deploys go to the SAME bws-main Worker, so its deployment history in the
Cloudflare dashboard keeps every version (incl. the old manual deploy a6cd90f6).
If a Git deploy ever looks wrong: Workers & Pages -> bws-main -> Deployments ->
pick a known-good version -> Rollback. Instant, no data loss. This is why the
migration carried zero real risk to the live site.
https://bwscomputerservice.com - homepage loads (incognito to skip cache)https://bwscomputerservice.com/support - support page loadscurl -I https://bwscomputerservice.com/support - all security headers presentdomain-monitor.sh (existing watcher) will alert if headers ever regress| What | Where |
|---|---|
| Local repo | ~/Projects/bws-site/ |
| GitHub repo | github.com/NeuralNetworkTrader/bws-site (private) |
| Homepage | public/index.html -> / |
| Support page | public/support.html -> /support |
| Security headers file | public/_headers |
| Wrangler config | wrangler.jsonc (repo root) |
| Cloudflare Worker | Workers & Pages -> bws-main |
| HSTS setting | Cloudflare zone -> SSL/TLS -> Edge Certificates |
| Zoho support portal | https://assist.zoho.com/portal/bwscomputerservice |
bwscomputerservice, matching the singular domain)./support from the homepage nav so clients can find it.robots.txt / sitemap.xml in public/ if SEO matters.