Shopify Age Verification Popup
A date-of-birth or yes/no age gate, remembered for repeat visits, scoped to the products that need it.
Your details
Your snippet file (snippets/age-verification.liquid)
{%- comment -%}
Cookie-based age gate. Client-side only: a good-faith check, not identity verification.
{%- endcomment -%}
{%- assign ea_show_gate = true -%}
{%- if ea_show_gate -%}
{%- style -%}
.ea-agegate{position:fixed;inset:0;z-index:100;background:rgba(0,0,0,.75);display:flex;align-items:center;justify-content:center;padding:1rem}
.ea-agegate[hidden]{display:none}
.ea-agegate__card{background:#fff;border-radius:12px;max-width:380px;width:100%;padding:1.75rem;text-align:center}
.ea-agegate__heading{margin:0 0 .5rem;font-size:1.25em}
.ea-agegate__body{margin:0 0 1.25rem;color:#555;font-size:14px}
.ea-agegate__dob{display:flex;gap:.5rem;justify-content:center;margin-bottom:1rem}
.ea-agegate__dob select{font:inherit;padding:.5em;border:1px solid #ccc;border-radius:6px}
.ea-agegate__actions{display:flex;gap:.5rem;justify-content:center}
.ea-agegate__btn{padding:.65em 1.4em;border-radius:6px;border:0;font:inherit;font-weight:600;cursor:pointer}
.ea-agegate__btn--primary{background:#111;color:#fff}
.ea-agegate__btn--secondary{background:#eee;color:#111}
.ea-agegate__error{color:#b3352c;font-size:13px;margin-top:.75rem}
.ea-agegate__error[hidden]{display:none}
{%- endstyle -%}
<div id="ea-age-gate" class="ea-agegate" data-mode="dob" data-min-age="18" data-cookie-days="30" data-decline="close" data-redirect="https://www.google.com" hidden>
<div class="ea-agegate__card">
<h2 class="ea-agegate__heading">Are you old enough?</h2>
<p class="ea-agegate__body">You must be of legal age to view this site.</p>
<form>
<div class="ea-agegate__dob">
<select name="ea-day" aria-label="Day"><option value="">Day</option><option value="1">1</option><option value="2">2</option><option value="3">3</option><option value="4">4</option><option value="5">5</option><option value="6">6</option><option value="7">7</option><option value="8">8</option><option value="9">9</option><option value="10">10</option><option value="11">11</option><option value="12">12</option><option value="13">13</option><option value="14">14</option><option value="15">15</option><option value="16">16</option><option value="17">17</option><option value="18">18</option><option value="19">19</option><option value="20">20</option><option value="21">21</option><option value="22">22</option><option value="23">23</option><option value="24">24</option><option value="25">25</option><option value="26">26</option><option value="27">27</option><option value="28">28</option><option value="29">29</option><option value="30">30</option><option value="31">31</option></select>
<select name="ea-month" aria-label="Month"><option value="">Month</option><option value="1">1</option><option value="2">2</option><option value="3">3</option><option value="4">4</option><option value="5">5</option><option value="6">6</option><option value="7">7</option><option value="8">8</option><option value="9">9</option><option value="10">10</option><option value="11">11</option><option value="12">12</option></select>
<select name="ea-year" aria-label="Year"><option value="">Year</option><option value="2026">2026</option><option value="2025">2025</option><option value="2024">2024</option><option value="2023">2023</option><option value="2022">2022</option><option value="2021">2021</option><option value="2020">2020</option><option value="2019">2019</option><option value="2018">2018</option><option value="2017">2017</option><option value="2016">2016</option><option value="2015">2015</option><option value="2014">2014</option><option value="2013">2013</option><option value="2012">2012</option><option value="2011">2011</option><option value="2010">2010</option><option value="2009">2009</option><option value="2008">2008</option><option value="2007">2007</option><option value="2006">2006</option><option value="2005">2005</option><option value="2004">2004</option><option value="2003">2003</option><option value="2002">2002</option><option value="2001">2001</option><option value="2000">2000</option><option value="1999">1999</option><option value="1998">1998</option><option value="1997">1997</option><option value="1996">1996</option><option value="1995">1995</option><option value="1994">1994</option><option value="1993">1993</option><option value="1992">1992</option><option value="1991">1991</option><option value="1990">1990</option><option value="1989">1989</option><option value="1988">1988</option><option value="1987">1987</option><option value="1986">1986</option><option value="1985">1985</option><option value="1984">1984</option><option value="1983">1983</option><option value="1982">1982</option><option value="1981">1981</option><option value="1980">1980</option><option value="1979">1979</option><option value="1978">1978</option><option value="1977">1977</option><option value="1976">1976</option><option value="1975">1975</option><option value="1974">1974</option><option value="1973">1973</option><option value="1972">1972</option><option value="1971">1971</option><option value="1970">1970</option><option value="1969">1969</option><option value="1968">1968</option><option value="1967">1967</option><option value="1966">1966</option><option value="1965">1965</option><option value="1964">1964</option><option value="1963">1963</option><option value="1962">1962</option><option value="1961">1961</option><option value="1960">1960</option><option value="1959">1959</option><option value="1958">1958</option><option value="1957">1957</option><option value="1956">1956</option><option value="1955">1955</option><option value="1954">1954</option><option value="1953">1953</option><option value="1952">1952</option><option value="1951">1951</option><option value="1950">1950</option><option value="1949">1949</option><option value="1948">1948</option><option value="1947">1947</option><option value="1946">1946</option><option value="1945">1945</option><option value="1944">1944</option><option value="1943">1943</option><option value="1942">1942</option><option value="1941">1941</option><option value="1940">1940</option><option value="1939">1939</option><option value="1938">1938</option><option value="1937">1937</option><option value="1936">1936</option><option value="1935">1935</option><option value="1934">1934</option><option value="1933">1933</option><option value="1932">1932</option><option value="1931">1931</option><option value="1930">1930</option><option value="1929">1929</option><option value="1928">1928</option><option value="1927">1927</option></select>
</div>
<div class="ea-agegate__actions">
<button type="submit" class="ea-agegate__btn ea-agegate__btn--primary">Enter site</button>
</div>
</form>
<p class="ea-agegate__error" data-error hidden>Sorry, you don't meet the minimum age to enter.</p>
</div>
</div>
<script>
(function () {
var el = document.getElementById('ea-age-gate');
if (!el) return;
function getCookie(name) {
return document.cookie.split('; ').reduce(function (r, c) { var p = c.split('='); return p[0] === name ? decodeURIComponent(p[1]) : r; }, null);
}
function setCookie(name, value, days) {
document.cookie = name + '=' + value + '; max-age=' + days * 86400 + '; path=/; samesite=lax';
}
if (getCookie('ea-age-verified') === '1') return;
el.hidden = false;
document.documentElement.style.overflow = 'hidden';
function confirmOk() {
setCookie('ea-age-verified', '1', Number(el.dataset.cookieDays) || 30);
el.hidden = true;
document.documentElement.style.overflow = '';
}
function decline() {
if (el.dataset.decline === 'redirect' && el.dataset.redirect) { window.location.href = el.dataset.redirect; return; }
var err = el.querySelector('[data-error]');
if (err) err.hidden = false;
}
if (el.dataset.mode === 'yesno') {
var yes = el.querySelector('[data-yes]');
if (yes) yes.onclick = confirmOk;
var no = el.querySelector('[data-no]');
if (no) no.onclick = decline;
} else {
var form = el.querySelector('form');
if (form) form.onsubmit = function (e) {
e.preventDefault();
var d = Number(el.querySelector('[name=ea-day]').value);
var m = Number(el.querySelector('[name=ea-month]').value);
var y = Number(el.querySelector('[name=ea-year]').value);
if (!d || !m || !y) return;
var dob = new Date(Date.UTC(y, m - 1, d));
var today = new Date();
var age = today.getUTCFullYear() - dob.getUTCFullYear() - (today.getUTCMonth() < m - 1 || (today.getUTCMonth() === m - 1 && today.getUTCDate() < d) ? 1 : 0);
if (age >= (Number(el.dataset.minAge) || 18)) confirmOk(); else decline();
};
}
})();
</script>
{%- endif -%}To install: in Shopify admin go to Online Store → Themes, click … next to your theme → Edit code. Under Snippets choose Add a new snippet, name it as shown above, paste this code and save. Add {% render 'age-verification' %} right after the opening <body> tag in theme.liquid, so it appears before the page's content.
This is a client-side, good-faith gate. It doesn't verify a real identity or birth date. Check whether your market requires a stricter, ID-based check for this product.
Live preview
Cookie-based, scoped to what needs it
The popup blocks the page (scroll and interaction) until the shopper confirms their age, either by typing a date of birth or answering yes/no, whichever you choose. Once confirmed, it's remembered in a cookie for the number of days you set, so returning visitors within that window aren't asked again.
Set it to show storewide, or only when the shopper is on a specific collection or viewing a product with a specific tag, so you're not gating an entire store over one age-restricted category.
Adding it to your theme
- 01
Add the snippet
Online Store → Themes → Edit code → Snippets → Add a new snippet named
age-verification, paste the code. - 02
Include it early
Add
{% render 'age-verification' %}right after the opening<body>tag intheme.liquid, so it appears before the page's content to avoid a flash of the page underneath.
This is a client-side gate: it improves compliance and shows good faith, but it doesn't verify a real identity or birth date. If you sell a product with strict legal age-verification requirements in your market, check whether your jurisdiction requires a more rigorous, ID-based check.
Questions people ask
Can a shopper get around this by clearing cookies?
Yes, and by disabling JavaScript. As noted above, this is a good-faith, client-side gate, not a hard security control.
Does the date-of-birth mode store the birth date anywhere?
No, it only stores a yes/no "verified" flag in the cookie once the entered date clears your minimum age; the date itself isn't saved or sent anywhere.
What happens if they say they're under the minimum age?
Whatever you set as the decline action: either they're redirected to a page you choose (for example a general age-appropriate page) or the popup simply stays up.
Will this slow down my page?
It's a small snippet with no external requests, so the impact is minimal; the main cost is the same as any content shown immediately on load.
Related tools
Last reviewed 30 September 2026.
