WebProCMS ships with a built-in Age Verification Gate for sites that sell or discuss age-restricted products — breweries, wineries, distilleries, tobacco/vape shops, cannabis dispensaries, adult-adjacent content. It's off by default and adds zero overhead to sites that don't use it. When enabled, a full-screen overlay blocks the site (or just selected pages) until the visitor confirms their age, remembers the choice for a configurable number of days, and redirects visitors who fail the check away from the site.
What it does
- Full-screen gate — an opaque overlay covering the whole page, styled with the site's brand colors and following light/dark mode automatically. Optionally shows the site's branding logo on the card.
- Two verification modes:
- Simple confirmation — two buttons: "I am 21 or older" / "I am under 21" (the number follows your configured minimum age).
- Date of birth — the visitor enters their birth date and the gate computes their age against the configured minimum (default 21).
- Region-aware minimum age — optionally apply a different minimum age by visitor country (e.g. 18 in the EU, 21 in the US). Rules map comma-separated ISO country codes to an age; the shorthand
EUcovers the EU/EEA plus the UK and Switzerland; the first matching rule wins. Country detection uses the same CDN headers as Cookie Consent (CloudflareCF-IPCountry, CloudFront, Vercel) — visitors without one get the default minimum age. - Remembers the choice — a first-party cookie (
wpcms_age_ok) keeps verified visitors from seeing the gate again for a configurable duration (default 30 days). Changing the mode, minimum age, or region rules automatically re-gates everyone whose cookie predates the change. - Bounce URL — visitors who fail the check see a configurable deny message, then are redirected to a configurable URL (default
https://www.google.com). - Site-wide, per-page, or per content type — gate the whole site, only specific paths (e.g. only
/shopand certain blog posts), or whole content types (the type's listing page and every item page under it). - Customizable copy — headline, body text, button labels, and deny message are all editable. Use
:ageanywhere in the copy to insert the configured minimum age (with region rules active, each visitor sees their region's age). - Analytics goals — when the Analytics feature is on, every pass and deny is counted in the conversion goal registry (Analytics → Conversions, goal types
age_pass/age_deny), so you can see how much traffic the gate turns away. Counts are anonymous, respect DNT/GPC, and exclude logged-in CMS staff.
How to turn it on
- Dashboard → Settings → Features → enable Age Verification Gate.
- Dashboard → Settings → Age Verification (appears in the sidebar once enabled) → configure mode, minimum age, scope, and copy.
Architecture: how the cache stays intact
WebProCMS uses Spatie's response cache for the public site — every page is cached once and served to every visitor. Like the Cookie Consent system, the gate is designed to never vary the cached HTML by visitor:
- The overlay markup and its configuration payload are identical for every visitor of a given URL, baked into the cached HTML. When region rules are active the payload also carries the raw copy templates (
:ageunsubstituted) — still identical for everyone. - The gate is server-rendered visible (fail closed). A tiny inline script in
<head>runs before first paint: if the visitor's remember-cookie is valid it hides the overlay (html.age-ok) — so returning visitors never see a flash of the gate, and new visitors never see a flash of the content. - Every per-visitor decision (cookie check, age math, deny redirect) happens client-side in ~2 KB of JS. With region rules off, there is no per-visitor server round trip at all. With region rules on, the JS fetches the visitor's effective age from
/_age-gate/init— the same uncached, browser-cached (~30 min) init-endpoint pattern the cookie consent banner uses — then re-renders the gate copy with that age. Server copy always renders with the default age, so the gate stays fail-closed even if the fetch fails. - Pass/deny counts flow through a fire-and-forget
sendBeaconto/_age-gate/hit(the same posture as the shared A/B testing beacon: always answers 204, rate-limited, DNT-respecting, staff-skipping) into the Analytics goal registry. - Per-page and per-content-type scoping is resolved per URL on the server, which is safe because the response cache is keyed by URL. Pages outside the scope carry zero gate bytes.
Scope: site-wide or selected pages
Site-wide (default): every public page is gated.
Selected pages only: list one path per line under Gated page paths. * works as a wildcard:
/shop
/shop/*
/blog/our-new-imperial-stout
/shopgates only the shop page;/shop/*gates everything under it. Use both lines to gate the section and its landing page.- Language-prefixed URLs (
/es/shop) match their base pattern automatically. - Individual pages can also be toggled from the page editor: Page Settings → Require age verification (shown when the addon is enabled). The toggle edits the same path list.
- Whole content types can be gated with checkboxes under Gate whole content types — the type's listing page, item pages, and taxonomy pages are all covered via the type's URL prefix, so new items are gated automatically with no path patterns to maintain. (A page covered by a gated content type shows as gated in the editor's Page Settings; ungate it by unchecking the type, not the per-page toggle.)
- Other dynamic URL groups are gated with a wildcard (
/blog/*) or by listing individual paths. Automatic gating driven by a content tag (e.g. "every post tagged 21+") is a possible future enhancement.
Search engines and crawlers
Gated content stays indexable. The gate is a client-side overlay, not a server block — the full page HTML (content, links, structured data) is present in the DOM underneath the overlay, and crawlers receive exactly the same HTML as everyone else. Google's crawler does not click buttons or fill in dates, so it indexes the content behind the gate normally. (Be aware the overlay may occasionally appear in rendered snapshots/screenshots of the page.)
This matches how mainstream age-gated brands handle SEO. If you need genuinely private content that crawlers and unauthorized visitors can never fetch, that's what Memberships page gating is for — the age gate is a compliance/deterrence measure, not access control.
The remember cookie
- Name:
wpcms_age_ok; contents: a version number, a configuration fingerprint, and a timestamp. No personal data — the visitor's actual birth date is never stored or sent anywhere. - Duration: configurable, default 30 days.
- It is a strictly necessary cookie. It never loads third-party code and exists solely to honor the visitor's own choice, so it is not gated behind the Cookie Consent system's analytics/marketing categories — it works even for visitors who reject all optional cookies.
Interplay with Cookie Consent
When both features are enabled, the age gate takes priority: the overlay renders above the consent banner, and the banner stays hidden until the visitor passes the gate. Once passed, the consent banner appears as normal. No configuration needed — this is automatic.
Failing the check
Clicking the deny button (or entering an underage birth date) shows the configurable deny message for a moment, then redirects the browser to the bounce URL. No "failed" cookie is set — a visitor who arrives again later can retry (deliberate: age gates are deterrents, and permanently locking out someone who misclicked is worse than allowing a retry).
Configuration reference
All settings live under Dashboard → Settings → Age Verification (admin only):
| Setting | Default | Notes |
|---|---|---|
| Verification mode | Simple confirmation | Or date of birth |
| Minimum age | 21 | 1–99; used by both modes and the :age copy placeholder |
| Region-aware minimum age | Off | Ordered rules: comma-separated ISO country codes (or EU) → age; first match wins; needs a CDN country header |
| Remember choice (days) | 30 | 1–730 |
| Redirect underage visitors to | https://www.google.com |
http/https URLs only |
| Apply the gate to | Entire site | Or selected pages: path list + whole-content-type checkboxes |
| Show the site logo | On | Uses the branding logo; hidden when none is set |
| Headline / body / buttons / deny message | Sensible defaults | :age inserts the minimum age (the visitor's regional age when rules are active) |
Saving settings — and toggling the feature on the Features page — clears the public response cache so changes appear immediately. Gate copy localizes with the public site the same way the cookie banner does (per-language age_verification.{key}__{lang} settings).
What it's not
- Not real age verification. Like every mainstream age gate, it trusts the visitor's answer. It is a compliance/deterrence measure, not identity verification — no ID checks, no third-party verification service.
- Not legal advice. Age-restriction requirements vary by jurisdiction and industry; operators should confirm their obligations with counsel.
- Not access control. Content remains in the page source and indexable (see above). Use Memberships gating for genuinely private content.
- Not usable without JavaScript. For no-JS visitors the overlay is server-rendered visible and cannot be dismissed (fail closed) — the safe default for a compliance feature.