Ki-Ki

Web foundations for SMEs

WordPress helpers

Free 18+ age gate for WordPress, small PHP function, no plugin

If your WordPress site covers medical cannabis, justice, trauma, or any other adult topics, you probably want a clean 18 plus notice without installing yet another plugin. This snippet gives you a simple age gate that lives in your codebase, not your plugin pile.

It shows a full screen 18 plus overlay, records consent with localStorage or a cookie if you prefer, and can send a small event to Plausible so you know how often it fires. You stay in control of the wording, the styling, and the data that is collected.

Free snippet No plugin required Accessibility aware

What this WordPress age gate actually does

Clear message, minimal friction

Visitors see a full screen overlay with a short 18 plus notice and a primary button that confirms they are old enough. There is also a secondary link that lets them leave the site entirely. You can rewrite the wording to match your tone of voice and regulatory environment.

Unlike some heavy age verification plugins, this pattern keeps the experience simple. It does not ask for birth dates or personal data. It just gives you a visible line in the sand and evidence that you warned people before they continued.

Small footprint in your theme

The entire gate is one PHP function that hooks into wp_footer. It writes a short CSS block, the overlay markup, and a self contained JavaScript function. You can treat it like any other piece of theme code and commit it to your version control.

Logged in editors and admins are skipped so your own staff are not blocked while they work. If you use a staging environment, you can keep the same pattern there and it will behave in the same way.

How to install the age gate on your WordPress site

1

Decide where to put the code

The simplest option is to paste the function into your theme functions.php. A cleaner option is to create a tiny must use plugin so the age gate survives future theme changes. If that language is unfamiliar, I can help you choose a path that fits your setup.

2

Update the wording and links

Replace the example text in the overlay with a short description of your site, your audience, and any legal or professional caveats. Point the privacy and terms links at your own policies. Keep the sentences clear and avoid legal theatre for its own sake.

3

Decide on localStorage or cookie

By default the snippet records confirmation in localStorage. If you prefer a cookie, flip one flag in the script and it will set a short, simple consent cookie instead. Both approaches are much lighter than full identity checks and easier to explain in your privacy notice.

4

Test with real visitors in mind

Once you have deployed the code, test it with JavaScript enabled and disabled, on mobile and desktop, and with a screen reader if possible. Make sure the 18 plus gate is obvious, easy to confirm, and easy to leave. If anything feels clumsy, adjust the wording or spacing.

Copy paste age gate snippet for WordPress

This is the full age gate function. It is written to be readable so you can see what it does and tweak it. You are free to use, edit, and adapt this pattern on your own projects. If you share it elsewhere, a small nod back to Ki-Ki is appreciated but not required.

<?php
/**
 * Simple 18+ age gate for WordPress
 * Free snippet from https://ki-ki.co.uk/
 *
 * Drop this into your theme functions.php
 * or into a small must use plugin.
 */

add_action('wp_footer', function () {

    // Skip for logged in editors and admins so your own staff are not blocked
    if ( current_user_can('edit_posts') ) {
        return;
    }
    ?>
    <style>
      #age-gate {
        position: fixed;
        inset: 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 1rem;
        background: rgba(0,0,0,0.95);
        color: #fff;
        z-index: 99999;
        text-align: center;
        padding: 2rem;
      }
      #age-gate h1 {
        color: #d4af37;
        margin: 0 0 .25rem 0;
        font-size: 1.75rem;
      }
      #age-gate p {
        max-width: 48rem;
        margin: 0 auto;
        line-height: 1.5;
      }
      #age-gate .cta {
        margin-top: .75rem;
        padding: .75rem 2rem;
        font-size: 1.1rem;
        font-weight: 700;
        background: #d4af37;
        color: #000;
        border: 0;
        border-radius: .5rem;
        cursor: pointer;
      }
      #age-gate .secondary {
        display: inline-block;
        margin-top: .5rem;
        color: #ccc;
        text-decoration: underline;
      }
      body.age-gate-active {
        overflow: hidden;
      }
      body.age-ok #age-gate {
        display: none;
      }
    </style>

    <div id="age-gate" role="dialog" aria-modal="true" aria-labelledby="age-gate-title" aria-describedby="age-gate-desc">
      <h1 id="age-gate-title">18+ content notice</h1>
      <p id="age-gate-desc">
        This site covers adult or sensitive topics and is intended for visitors aged 18 or over.
        By continuing, you confirm that you are old enough to view this material and that you accept
        the way we handle privacy and terms on this site.
        <br />
        <strong>Update this wording so it accurately reflects your site</strong>.
      </p>
      <p>
        By entering, you confirm that you are 18 or over and that you accept our
        <a href="/privacy-policy/" style="color:#d4af37; text-decoration: underline;">Privacy Policy</a>
        and
        <a href="/terms/" style="color:#d4af37; text-decoration: underline;">Terms of Use</a>.
        Update these links so they point at your own pages.
      </p>
      <button id="age-gate-confirm" class="cta" type="button">I am 18+</button>
      <a class="secondary" href="https://www.google.com" rel="nofollow noopener">No, take me away</a>

      <noscript>
        <p style="margin-top:1rem;color:#f4d03f;">
          JavaScript is required to confirm age. Please enable JavaScript or leave this site.
        </p>
      </noscript>
    </div>

    <script>
      (function() {
        // Set to true if you prefer to also set a cookie
        var USE_COOKIE = false;
        var COOKIE_NAME = "ageConfirmed";
        var COOKIE_VAL  = "yes";
        // One year, strict cookie scope. Adjust if needed.
        var COOKIE_ATTR = "Max-Age=31536000; Path=/; Secure; SameSite=Strict";

        // Name used for optional Plausible analytics event
        var EVENT_NAME  = "Age Gate Confirmed";

        function hasLocalOK() {
          try {
            return localStorage.getItem("ageConfirmed") === "yes";
          } catch(e) {
            return false;
          }
        }

        function setLocalOK() {
          try {
            localStorage.setItem("ageConfirmed", "yes");
          } catch(e) {}
        }

        function hasCookieOK() {
          return (document.cookie || "").indexOf(COOKIE_NAME + "=" + COOKIE_VAL) !== -1;
        }

        function setCookieOK() {
          document.cookie = COOKIE_NAME + "=" + COOKIE_VAL + "; " + COOKIE_ATTR;
        }

        function isConfirmed() {
          return hasLocalOK() || hasCookieOK();
        }

        function enableGate() {
          document.body.classList.add("age-gate-active");
          var btn = document.getElementById("age-gate-confirm");
          if (btn) {
            btn.focus();
          }
        }

        function disableGate() {
          document.body.classList.remove("age-gate-active");
          document.body.classList.add("age-ok");
        }

        if (isConfirmed()) {
          disableGate();
        } else {
          enableGate();
        }

        function confirmAge() {
          // Optional analytics hook. Remove this block if you do not use Plausible.
          if (typeof window.plausible === "function") {
            try {
              window.plausible(EVENT_NAME);
            } catch(e) {}
          }

          setLocalOK();
          if (USE_COOKIE) {
            setCookieOK();
          }
          disableGate();
        }

        var confirmBtn = document.getElementById("age-gate-confirm");
        if (confirmBtn) {
          confirmBtn.addEventListener("click", confirmAge);
        }
      })();
    </script>
    <?php
});

If you prefer not to use the Ki-Ki gold colour, change #d4af37 in the CSS block to your own accent colour. Keep the contrast strong so the age confirmation button is obvious to visitors.

How this age gate behaves with caching and Cloudflare

Because the gate is rendered by WordPress in the footer, it appears on every front end page where the code runs. The decision to show or hide it is handled entirely in the browser using localStorage and an optional cookie. That means you can keep aggressive Cloudflare caching in place without breaking the logic.

If you use Cloudflare firewall rules, you can add basic protection in front of WordPress as usual. The age gate does not expose any extra endpoints or authentication checks. It is just a visual control that helps you prove you took reasonable steps to warn visitors about the content they are choosing to view.

Related Ki-Ki articles and support

  • Static websites vs WordPress

    A practical comparison of static sites and WordPress for small organisations, including security, maintenance, and costs. Useful context if you are weighing up a longer term rebuild.

  • Privacy policy that does not backfire

    Guidance on writing a privacy policy that matches what your site actually does, so you can point regulators and visitors towards something accurate instead of boilerplate.

  • Evidence grade logs

    How to set up logging and simple dashboards that help you answer questions from boards, funders, or regulators about who accessed what and when.

Common questions about 18+ gates on WordPress

Is an 18 plus gate legally required for adult topics?

Law varies by country and by sector. In the UK, what you usually need is a clear, honest warning and content that is presented in a serious, proportionate way. This snippet helps you show that you gave a visible notice and that visitors chose to continue. It is not a substitute for legal advice, but it is often better than having no boundary at all.

Where do I paste this code inside WordPress?

Place the function in your active theme functions.php file or inside a small must use plugin. If you have a managed hosting partner, ask them which option fits their deployment process. Either way, keep a copy of the code somewhere safe so you can redeploy it if the theme changes.

Does this age gate set cookies by default?

No. By default the gate records confirmation in localStorage, which does not leave a traditional cookie. If you want a cookie as well, set USE_COOKIE to true in the script. Either choice is much lighter than a full tracking or profiling setup, which keeps your compliance story simpler.

Will it conflict with my caching plugin or CDN?

It should not. The markup is rendered in the footer like any other part of the page, and the decision to show or hide the overlay is handled in the browser. As long as your site serves the same HTML to each anonymous visitor, the gate will behave consistently even with page caching and a content delivery network such as Cloudflare.

Can I change the styling to match my brand?

Yes. The CSS is included in the snippet so you can change the background colour, button shape, and typography. Keep the contrast high for accessibility, and avoid hiding the 18 plus statement behind clever design. The goal is clarity rather than theatre.

Can Ki-Ki help implement or review this for my organisation?

Yes. I can drop into your existing WordPress setup, check how this snippet fits alongside your theme, plugins, and Cloudflare configuration, and give you a short written summary with suggested improvements. That can include wider security fixes, not just the age gate.