DEV Community

Amit Feldman
Amit Feldman

Posted on Edited on

All six security headers fixed in a day — the Macless before/after

Earlier today I left a findings-first comment on a launch writeup here: a passive scan of the product's site, the top findings with the concrete fix, and an offer of a free re-scan once deployed. No pitch — the scan is the conversation.

The maker read it, put Cloudflare in front of his GitHub Pages site, and closed every single finding within hours. This is the before/after.

The launch

Macless by @maclessdev — a GitHub-Actions pipeline, signing scripts, and notes for building and submitting an iOS app to the App Store without owning a Mac. The marketing page lives at macless.dev, hosted on GitHub Pages.

Before (scan, Aug 16): 10 passed / 4 warnings / 2 failures

The base was decent: TLS clean (Let's Encrypt, TLSv1.3), fast response (266 ms), title, meta description, exactly one H1, robots.txt and sitemap.xml live.

The gap was the entire security-header layer — all six missing:

  • No HSTS — every first visit over HTTP was downgrade-able
  • No Content-Security-Policy — no XSS mitigation layer at all
  • No X-Frame-Options — clickjacking exposure
  • No X-Content-Type-Options, Referrer-Policy, Permissions-Policy

The frustrating part: none of this is application work. GitHub Pages doesn't let you set custom headers, full stop — so the fix isn't a code change, it's an architecture tweak.

The fix: Cloudflare in front, two Transform Rules

He put Cloudflare in front of the Pages site (free tier) and set the headers at the edge with Transform Rules — no deploy, no code, DNS plus config.

After (re-scan, same day — verified twice): 16 passed / 0 warnings / 0 failures

  • HSTS: max-age=15552000 (180 days)
  • CSP: default-src 'self'; script-src 'none'; style-src 'self'; img-src 'self'; font-src 'self'; frame-ancestors 'none'; base-uri 'self'; object-src 'none'; form-action 'self'
  • X-Frame-Options: DENY
  • X-Content-Type-Options: nosniff
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy: geolocation, microphone, camera all locked down

That CSP deserves a second look. script-src 'none' is bolder than most production sites dare to run — it says this page executes zero JavaScript, which for a static marketing page is exactly true and exactly right. frame-ancestors 'none' plus XFO DENY is belt-and-suspenders clickjacking defense. This isn't a header set copied from a blog post; it's a header set matched to what the page actually does.

Three takeaways for anyone launching on GitHub Pages

1. Pages can't set headers — put a proxy in front. GitHub Pages serves your files and nothing else. If your launch page lives there, the security-header layer doesn't exist until you add Cloudflare (or any CDN that can mutate response headers) in front of it.

2. The fix is a Transform Rule, not a sprint. Cloudflare → Rules → Transform Rules → Modify Response Header. One rule can set every static header. Total elapsed work: minutes. The entire "after" column above required zero commits.

3. Match CSP to reality, then tighten. A static page can run script-src 'none'. An app shell can't. Start from what the page genuinely loads, enforce that, and resist the urge to cargo-cult a permissive policy you saw elsewhere.

Why I'm telling this story

Because this is the loop that works: findings → fix → verified re-scan. @maclessdev didn't pay anyone for this — the first scan and the re-scan were free, and his launch page now passes checks that show up verbatim in enterprise security questionnaires.

I run these scans findings-first on launch posts here regardless of whether anyone buys anything. If you want the full deep pass — auth-flow edge cases, header drift across routes, perf budget, prioritized fix list with stack-specific snippets — that's the Launch-Ready Audit ($19, 48h turnaround): https://afeldman2.gumroad.com/l/zxpluh?utm_source=devto&utm_medium=article&utm_campaign=casestudy-macless
The Deep Dive ($99) adds a written remediation plan + re-scan verification: https://afeldman2.gumroad.com/l/wmdfxb?utm_source=devto&utm_medium=article&utm_campaign=casestudy-macless-deepdive

And if I commented on your launch and you've shipped the fixes, say so in the thread. The re-scan is free, and if the before/after is this clean, I'll write about yours next.


Postscript (September 2026): the layer a header scan can't see

After this article went live, @maclessdev took the same findings-first loop one level deeper — into the CI pipeline itself, which for Macless is the product (signing certificates, App Store Connect keys, GitHub Actions workflows). He ran the pass himself and it turned up two real gaps, both now fixed:

  • No explicit permissions: block on the workflows. Older repos lean on the default GITHUB_TOKEN scope, which is broader than most people assume. Explicit per-workflow permissions close that.
  • No .gitignore covering signing material. One stray git add -A and a cert or provisioning profile is in history forever.

Worth stating plainly: the header scan in this article says nothing about any of that. A page can be 16/16 green while a workflow dumps secrets into logs or a token is scoped wider than it needs to be. The checks that caught these: full git history on both repos for committed certs/keys (none, ever), how secrets reach the workflows (environment variables, never shell strings), whether anything logs them (no), and GITHUB_TOKEN scoping. His signing credentials live in encrypted GitHub secrets, and none of the workflows trigger on pull requests — which is the usual way a public repo leaks them.

If your product's crown jewels live in a pipeline and not on the marketing page, that second pass is the one that matters.

Top comments (2)

Collapse
 
amitfeldman profile image
Amit Feldman

Thanks, that means a lot. The credit goes both ways — you shipped the fixes faster than most teams schedule the meeting, which is exactly why the before/after worked as a case study.

The permissions-block and .gitignore gaps really are the two I see most on "get it building first" projects — both are ten-minute fixes that only hurt when nobody ever takes the second pass you just did.

Offer stands for anyone reading: the free scan finds the obvious things, the paid pass finds the ones that bite later. Either way, build something people want to break into.

Collapse
 
maclessdev profile image
Jackson

Really appreciate this - the case study reads exactly how it happened, and the credit's more than fair given you did two full scans for free before any of it landed.

Good calls on the permissions block and the .gitignore too - those are the two gaps I'd bet show up most on projects that started as "just get it building" and never got a second pass looking at the CI side.

Add the postscript whenever's convenient, happy to have that context in there. And for anyone reading this who wants the deeper pass - auth-flow edge cases, header drift, perf budget - Amit's Launch-Ready Audit and Deep Dive are worth it. He found three real things on mine for free before I ever paid him a cent.

Thanks for the fast turnaround on all of it.