Cron-Based Expiration and Access Control — Watching the Deadline

How a scheduled job finds expired pages and unpublishes them, and what to return when someone opens one after the fact

cronauto expirationaccess controlscheduled jobsVercelexpired pages
6 min read

Introduction

Entering an expiration date doesn't make a page disappear by itself. Something still has to watch whether the deadline has arrived and unpublish the page when it has. That watching job goes to Vercel Cron (a scheduled-execution mechanism that automatically runs a process at a time you set).

This article covers how the scheduled job finds expired pages and takes them out of view, and what you should return when someone lands on one that has already expired. Behind the experience of content that disappears on its own, there is some quiet but important processing. Since these choices feed straight into how the site is operated, let's start with how the frequency was decided.

Designing the Job That Watches Deadlines

Why once a day is enough

The expiration check runs once a day. Campaign and event deadlines are nearly always day-level — "valid through this date" — and there is almost never a case that demands minute-level accuracy. The configuration is just a request to run this process at a fixed time every day.

Raise the frequency and you raise the number of runs, the effort of confirming it's still working, and the cost. Picking a cadence that isn't excessive for the requirement is part of keeping the system light. For the rare content that must stop the second it expires, setting a lifespan at save time and letting it auto-delete covers that case alongside.

How the scheduled job retires a page
It fires at the set time

Once a day, at the configured time, the expiration process is invoked automatically

It gathers the candidates

It pulls the list of stored content and compares each expiration date against the current time

It unpublishes what has expired

Anything past its deadline has its publish flag lowered; anything meant to go is deleted outright

It records the result

What was retired, and when, is logged so it can be traced later

The flow in one sentence: every day at a set time it compares every record, stops publishing the ones past their deadline, and leaves a record of what it did.

Detecting expiry is just comparing two datetimes

The detection itself is almost anticlimactic. Take each stored page's expiration date and compare it against the current time. If the deadline is in the past, it has expired. That's all. Because the expiration lives inside the data, the rule stays in one place.

To pull the list, the job uses the index of slugs prepared on the storage side. It walks every record, but the number of temporary pages is modest, so speed is a non-issue. Skipping clever optimizations means anyone reading the code later can still tell what it does.

Make sure repeated runs produce the same result

One thing worth being careful about when writing scheduled processing is making sure that running the same process any number of times gives the same result. The technical term is idempotency, though the name isn't the part you need to remember.

Scheduled jobs occasionally fire twice, or fail partway and get retried automatically. When that happens, re-retiring something already retired must not cause anything strange. "If past the deadline, stop publishing" is naturally unchanged by repetition, so it already satisfies this. Keep that property intact and you can design retries and error handling without worrying.

What to Show Users After Expiry

Return a 404, or show a notice?

When someone opens an expired page, there are broadly two choices. Return a 404 (the response meaning the page doesn't exist), or show a notice reading "this page is no longer available." Which one fits depends on the nature of the content.

For a purely disposable internal URL, a 404 is plenty. But for something like a campaign page that spread on social media and still gets clicked in good faith, a notice is the kinder answer. Rather than assuming expiry means an immediate 404, decide how the page ends and you'll spare people some needless confusion.

The table's point: whether the URL has circulated outside your organization is what changes the right answer.

Check the expiration again on every render

Because the job runs once a day, a gap opens between the moment a deadline passes and the next run. To keep a stale page from showing during that window, the public page also checks the expiration every time it renders.

Right after fetching the record, it compares the deadline to the current time and, if it has passed, treats the page as expired on the spot. The scheduled job is the one that clears things out in batches; the render-time check is the one that decides on the spot. Adding that second layer means the timing of expiry has no holes in it.

Combine "expires on a deadline" with "only some people can see it"

Some temporary pages need their audience narrowed in the first place — documents handed to a single business partner, or something shared internally for a short period. In those cases, on top of expiring by deadline, you control access itself: a slug that's hard to guess, or a simple viewing restriction.

Expiring on a deadline and restricting who can view are separate mechanisms. Combined, though, they widen the range of situations you can cover. Build it so that both cases — already expired, and not yet published — fail toward the safe side, and you can stop worrying about either.

Summary

Cron-based auto-expiration takes the simple job of watching deadlines and unpublishing, and does it every day in place of a person. The parts to get right: a once-a-day cadence matched to the requirement, processing that repeats safely, and the render-time double check. With those three in place, stale pages essentially stop appearing.

The other thing worth settling in advance is what expired pages show. Cut it off with a 404, or show a notice pointing to where to go next. Deciding that far means users who arrive after the fact aren't left with nothing.

How the data this job watches is stored, and where the whole mechanism actually earns its keep, are covered in the next articles.