PR #135 opened: feat: security headers + public sitemap
Summary
Closes the remaining technical-SEO gaps from the Xanthos audit that fall on the dev side. Stacks on top of the SSR work (#133, #134) — without SSR these would have been pointless because crawlers couldn't read anything anyway.
Security headers middleware (SetSecurityHeaders)
Wired into the web group. Sets HSTS (HTTPS-only), X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, and a Permissions-Policy that disables camera/microphone, scopes geolocation to first-party, and opts out of FLoC. Should bump Securityheaders.com grade from D toward A.
CSP intentionally deferred. Too much surface to land safely without a Report-Only soak first (Stripe + Reverb + Sanity + Sentry + Umami + R2 + Inertia inline scripts). Worth a follow-up PR with a tight policy in Content-Security-Policy-Report-Only for a couple of weeks before enforcing.
Public sitemap (/sitemap.xml)
Single-file XML. Includes:
- Homepage,
/cleaners,/blog,/faq,/contact - All searchable cleaner profiles (uses the existing
Searchablescope:visibility=true+onboarding_completed_at+ verified or company) - All Sanity-managed pages
- All blog posts
Cached for 1 hour.
robots.txtnow points at it.
This bumps the indexable URL count from "homepage + login" (per the audit) to homepage + 4 marketing pages + N cleaner profiles + Sanity content — a real number for Google to chew on.
Not in this PR: per-service / per-location landing pages. That's content + routing + copy work, weeks not days, and belongs in a separate scoping conversation.
Test plan
-
php artisan test tests/Feature/SitemapTest.php tests/Feature/Middleware/SetSecurityHeadersTest.php— 10 passed - Smoke nearby tests (Middleware, PageController, PublicJobController) — 24 passed
-
vendor/bin/pint --dirty— clean - After deploy:
curl -I https://dev.tidylinker.com/shows the new headers - After deploy:
curl https://dev.tidylinker.com/sitemap.xmlreturns valid XML containing the homepage and at least one cleaner profile URL - After deploy:
curl https://dev.tidylinker.com/robots.txtends withSitemap: https://tidylinker.com/sitemap.xml