Skip to content

feat(redirect): serve a friendly 410 page for expired links#34

Open
onamfc wants to merge 1 commit into
mainfrom
brandon/expired-link-page
Open

feat(redirect): serve a friendly 410 page for expired links#34
onamfc wants to merge 1 commit into
mainfrom
brandon/expired-link-page

Conversation

@onamfc

@onamfc onamfc commented Jul 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Expired links now show a proper page. Opening a short link past its expires_at returns HTTP 410 Gone with a lightweight HTML page — clock icon, "This link has expired", one line of guidance, and a small "Powered by LinkForty" footer. Previously expired links returned the same bare 404 {"error":"Link not found"} as short codes that never existed. [1]
  • Expired vs. never-existed is now distinguished. When the active-link lookup misses, a secondary query checks for a link with that short code whose expires_at has passed (both the template-slug and legacy URL forms). Since expired links keep their expires_at even after a housekeeping job deactivates them, the 410 page keeps serving indefinitely. Unknown short codes still 404. [1]
  • Expiry is now enforced on Redis cache hits. A link cached shortly before expiring used to keep redirecting for up to the 5-minute cache TTL; the redirect now re-checks expires_at after a cache read, so links stop at the exact expiration moment. [1]
  • The page carries noindex so expired links drop out of search engines, and it's served on whatever domain the short link uses. [1]
  • New exported helpers isLinkExpired() (null/invalid timestamps fail open) and generateExpiredLinkHTML(), both unit-tested. [1] [2]

Test plan

  • npm test — 145 tests pass, including new coverage for isLinkExpired (no expiry, future, past, exact-moment, unparseable) and the page content.
  • npm run build clean.
  • Manual: create a link with expiresAt in the near future, click it before expiry (redirect works), after expiry (410 page), and confirm an unknown short code still returns 404.

Expired short links previously returned the same bare 404 JSON as
nonexistent ones. Now they return HTTP 410 Gone with a lightweight,
unbranded 'This link has expired' page (noindex, Powered by LinkForty
footer).

- Distinguish expired from never-existed: when the active-link lookup
  misses, a secondary check finds links whose expires_at has passed —
  accurate even after a housekeeping job flips is_active off.
- Enforce expiry on cache hits too: a link cached shortly before its
  expiration no longer keeps redirecting for up to the cache TTL.
- Export isLinkExpired() and generateExpiredLinkHTML() with unit tests.
@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 67.64706% with 22 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/routes/redirect.ts 67.64% 22 Missing ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant