Every pull request that touches UI ends with the same comment: "can you post a screenshot?" Screenshots lie. Here's how to get a clickable, disposable preview on every CI run — with two lines of YAML and zero infrastructure on your side.
The two lines
The official action takes a .html file, a .zip, or a whole directory, hurls it at the API, and hands the live URL back as an output. It goes right after your build step:
.github/workflows/preview.yml
- uses: bitgate/hurl-page-action@v1 id: deploy with: path: distFine — four lines once you count the id and the with. The point stands: there is nothing to configure, because there is no project to set up on the other side. Every run gets its own subdomain, and the step exposes url and slug outputs for whatever you want to do next.
Getting the link in front of humans
A link nobody sees is a link nobody clicks. The cheapest surface is the job summary:
.github/workflows/preview.yml
- run: echo "### Preview: ${{ steps.deploy.outputs.url }}" >> "$GITHUB_STEP_SUMMARY"Prefer plain curl? The whole integration is one request — this is the same thing the action does under the hood:
publish-report.sh
URL=$(curl -fsS -X POST https://hurl.page/deploy \ -H "Content-Type: text/html" \ --data-binary @build/report.html | jq -r .url)echo "### Report: $URL" >> "$GITHUB_STEP_SUMMARY"No tokens to rotate
Anonymous deploys need no auth at all — nothing to put in your repo secrets, nothing to leak. If your pipeline deploys more than 10 times a minute from one IP, add an API key and the limit becomes 120/min per account.
Previews that clean up after themselves
Preview links are disposable by definition, so let them dispose of themselves. Anonymous drops live 7 days by default; pass ?ttl=86400 and they're gone after a day — usually plenty for a review cycle. When the expiry hits, the drop serves a 410 and an hourly sweep deletes the files. No orphaned previews piling up in a bucket you forgot about, no cleanup job to write.
ttlis in seconds, minimum 60- Free drops cap at 7 days; subscribers can set anything (or nothing — no expiry)
- Active drops expose their deadline via the
x-drop-expiresresponse header
Stable URLs for PRs
One thing random subdomains can't give you: the same URL across pushes. Reviewers bookmark the first link you post, then quietly review a stale build. Named drops fix that — deploy with ?name=pr-42 and the URL stays put while the content updates on every push. That one's covered in its own post.