You deploy a new feature. Everything looks fine. Two days later someone notices the PageSpeed score dropped from 88 to 51. Or a new Content-Security-Policy header broke something so the team just... removed it. Or the staging environment had no SEO meta tags, and somehow the PR merged to production anyway.
These are the kinds of regressions that slip through every team's review process — not because developers are careless, but because nobody is checking the right things at the right moment. The right moment is before the PR merges, on the preview URL, as part of the normal CI workflow.
In this guide you'll set up an automated pre-deploy scan using SiteBrief and GitHub Actions. Every pull request gets a comment with PageSpeed score, security header grade, SSL status, and SEO meta check — before a single line ships to production.
What gets checked
Step 1 — Get a SiteBrief API key
Create a free account at sitebrief.net/register, then go to Settings → API Keys and create a new key. It looks like sp_live_xxxxxxxx. You'll only see it once — save it.
Add it to your GitHub repo as a secret: Settings → Secrets and variables → Actions → New repository secret. Name it SITEBRIEF_API_KEY.
Step 2 — Add the workflow file
Create .github/workflows/sitebrief.yml in your repository. Pick the snippet that matches your deployment platform:
Vercel
name: SiteBrief Pre-deploy Scan
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
sitebrief:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Wait for Vercel preview
uses: patrickedqvist/wait-for-vercel-preview@v1.3.1
id: vercel
with:
token: ${{ secrets.GITHUB_TOKEN }}
max_timeout: 60
- name: SiteBrief scan
id: scan
run: |
RESULT=$(curl -s -X POST https://sitebrief.net/api/scan/preview \
-H "Authorization: Bearer ${{ secrets.SITEBRIEF_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"url": "${{ steps.vercel.outputs.url }}"}')
echo "markdown<<EOF" >> $GITHUB_OUTPUT
echo "$RESULT" | jq -r '.markdown' >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Post PR comment
uses: marocchino/sticky-pull-request-comment@v2
with:
message: ${{ steps.scan.outputs.markdown }}Netlify
name: SiteBrief Pre-deploy Scan
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
sitebrief:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Wait for Netlify preview
uses: probablyup/wait-for-netlify-action@3.2.0
id: netlify
with:
site-id: ${{ secrets.NETLIFY_SITE_ID }}
max-timeout: 60
- name: SiteBrief scan
id: scan
run: |
RESULT=$(curl -s -X POST https://sitebrief.net/api/scan/preview \
-H "Authorization: Bearer ${{ secrets.SITEBRIEF_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"url": "${{ steps.netlify.outputs.deploy-url }}"}')
echo "markdown<<EOF" >> $GITHUB_OUTPUT
echo "$RESULT" | jq -r '.markdown' >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Post PR comment
uses: marocchino/sticky-pull-request-comment@v2
with:
message: ${{ steps.scan.outputs.markdown }}Any other platform
If your CI already knows the preview URL — Render, Railway, Fly.io, a custom staging server — pass it directly:
- name: SiteBrief scan
id: scan
run: |
RESULT=$(curl -s -X POST https://sitebrief.net/api/scan/preview \
-H "Authorization: Bearer ${{ secrets.SITEBRIEF_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"url": "https://preview-${{ github.event.pull_request.number }}.staging.myapp.com"}')
echo "markdown<<EOF" >> $GITHUB_OUTPUT
echo "$RESULT" | jq -r '.markdown' >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUTStep 3 — What the PR comment looks like
After the first PR is opened, you'll see a comment like this automatically appended (and updated on every new push):
https://preview-42.myapp.comScanned: Sun, 31 May 2026 14:22:11 UTC
| Check | Status | Details |
|---|---|---|
| PageSpeed | ✅ pass | Score: 87/100 |
| Security Headers | ⚠️ warn | Grade C — missing: X-Frame-Options, CSP |
| SSL | ✅ pass | Valid certificate |
| SEO Meta | ✅ pass | ✓ title, ✓ description |
The comment is sticky — it updates in place on every new push. Your reviewers don't get a wall of duplicate comments as the PR evolves.
Optional: block merging on failures
By default the scan posts a comment but doesn't block the PR. If you want failures to block merging, add an exit code check and configure a required status check in your branch protection rules:
- name: SiteBrief scan
id: scan
run: |
RESULT=$(curl -s -X POST https://sitebrief.net/api/scan/preview \
-H "Authorization: Bearer ${{ secrets.SITEBRIEF_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"url": "${{ steps.vercel.outputs.url }}"}')
echo "markdown<<EOF" >> $GITHUB_OUTPUT
echo "$RESULT" | jq -r '.markdown' >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Fail the step if any checks failed
FAILED=$(echo "$RESULT" | jq -r '.failed')
if [ "$FAILED" -gt "0" ]; then
echo "❌ $FAILED check(s) failed — blocking merge"
exit 1
fiRun only the checks you care about
The checks parameter lets you skip checks you don't need. For a marketing site that doesn't control server headers, you might only care about PageSpeed and SEO:
-d '{"url": "...", "checks": ["pagespeed", "seo"]}'Available values: pagespeed, security, ssl, seo. Default is all four.
Why catch this before deployment?
Monitoring tools like SiteBrief are great at telling you when something is wrong in production. But there's a category of problem that monitoring can't easily reverse: the PageSpeed regression you didn't notice until three days after deployment. The security header that got removed during a config refactor. The SEO meta tags that someone deleted and nobody caught.
Pre-deploy scanning closes the loop. Monitoring watches what's live. The GitHub Actions scan watches what's about to go live. Together they cover the full deployment lifecycle.
- Monitoring — alerts you when the live site goes down or degrades
- Pre-deploy scan — blocks regressions before they ever reach the live site
- DevLab — automatically generates and opens a PR to fix detected issues