Skip to main content


So I kind of set up another level of security well not really more of a gate to help block more bots and scrapers that seem to bypass robots.txt and whatever enhancements and blocks you have in your .htaccess file, these currently are working on my #friendica instance fairly well so maybe they can help someone maybe they do nothing;

First at the very top of your index.php in the root of your friendica directory right after the <? I added the following code,
// --- ALIEN GATE START ---
$request = $_SERVER['REQUEST_URI'] ?? '';

// 0. Signed cookie (prevents bots forging it)
$secret_key = 'Your_Alien_Secret';
$valid_cookie = hash_hmac('sha256', 'verified', $secret_key);
$has_cookie = isset($_COOKIE['instance_access']) &&
              hash_equals($valid_cookie, $_COOKIE['instance_access']);

if (!$has_cookie) {

    // 1. Federation / machine endpoints (never gate)
    $is_fediverse =
        str_contains($request, '/.well-known/') ||
        str_contains($request, '/activitypub/') ||
        str_contains($request, '/api/') ||
        str_contains($request, '/assets/');

    // 2. Static assets (never gate)
    $is_static = preg_match('/\.(css|js|png|jpg|ico|svg|woff2)$/i', $request);

    // 3. Only gate the root URL
    if ($request === '/' && !$is_fediverse && !$is_static) {
        require 'gate.php';
        exit;
    }

    // 4. Prevent bypass via /index.php
    if ($request === '/index.php') {
        header('Location: /');
        exit;
    }
}
// --- ALIEN GATE END ---


then I created in the root directory again, gate.php
 <?php
// 1. VERIFICATION LOGIC
if (isset($_GET['nonce']) && isset($_GET['seed'])) {
    $nonce = (int)$_GET['nonce'];
    $seed = $_GET['seed'];
    $check_hash = hash('sha256', $seed . $nonce);

    // Verify hash matches the "000" requirement
    if (str_starts_with($check_hash, '000')) {
        $secret = 'Your_Alien_Secret';
$token  = hash_hmac('sha256', 'verified', $secret);
setcookie("instance_access", $token, time() + 86400 * 30, "/");
        header("Location: /index.php");
        exit;
    }
}

// 2. CHALLENGE UI
$seed = bin2hex(random_bytes(16));
?>
<html>
<head>
    <title>Access Verification</title>
    <link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Mono&display=swap" rel="stylesheet">
    <style>
        body {
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            font-family: 'Atkinson Hyperlegible Mono', monospace;
            background-color: #f4f4f9;
            color: #333;
        }
        .container {
            text-align: center;
            padding: 2rem;
            background: white;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>Your Instance Name</h2>
        <h3>Verification Required</h3>
        <p id="status">Your Computer Is Solving A Security Challenge To Prove You Are A Human...</p>
        <p>Your Computer Is Solving A Security Challenge To Prove You Are A Human...</p>
        <p>Your Privacy Matters, No Data Is Recorded...</p>
    </div>

    <script>
        const seed = "<?php echo $seed; ?>";
        let nonce = 0;

        async function sha256(message) {
            const msgUint8 = new TextEncoder().encode(message);
            const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);
            return Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('');
        }

        async function solve() {
            const startTime = Date.now();
            
            while (true) {
                let hash = await sha256(seed + nonce);
                if (hash.startsWith("000")) {
                    const elapsed = Date.now() - startTime;
                    const remaining = 2000 - elapsed;
                    
                    if (remaining > 0) {
                        document.getElementById('status').innerText = "Verifying human interaction...";
                        await new Promise(r => setTimeout(r, remaining));
                    }
                    
                    window.location.href = `?nonce=${nonce}&seed=${seed}`;
                    break;
                }
                nonce++;
            }
        }
        solve();
    </script>
</body>
</html>


in both places change Your_Alien_Secret for your actual matching secret, and maybe it will help kill some bot and scrapers from hitting your instance so hard;

⚖️ License (MIT)
Copyright (c) 2026 pasjrwoctx👽 (Philip A. Swiderski Jr.)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE

@Friendica Support @Friendica Developers @Friendica Admins

You can encourage my continued useless ideas, and by doing so your helping to feed, house and clothe a #disabled man living in #poverty, $5-10-15 It All Helps, via #cashapp at $woctxphotog or via #paypal at https://www.paypal.com/donate?campaign_id=5BN5MB5BVQL22
Could this be implemented as an add-on? I think it should be possible
@Carlos Solís I have no idea about making it an addon, personally I think that defeats the purpose, as this really acts like the first line of defense after .htaccess rules, before anything else even happens, at least that is how I saw it working;
@pasjrwoctx👽 I did not test it yet, but I believe you should exclude the /inbox route e.g. too?
@Melanie Wehowski the only reason to exclude the /inbox route is if you are finding a lot of bots and scrapers accessing your inbox before they scrape your profile, in theory friendicas rules and activitypubs rules should already have your inbox blocked from that activity, I created this because even with a very robust .htaccess file I was getting a lot of bots and scrapers accessing "GET" and "Profile" in my access logs, most where defeating my robots.txt and breaking the rules in .htaccess, this at the moment has killed them, first 2 days they all got 403's and then they just stopped trolling my instance at all; I think it could be customized to your individual needs, I made it shareable, I wont maintain it, it works for me and if by chance it helps anyone well all the better, by all means adapt and modify it to your instance's needs and if it works share it with the community, security and privacy in numbers I say;
@pasjrwoctx👽 Sorry, I mean the /inbox route from the check

$is_fediverse =
str_contains($request, '/.well-known/') ||
str_contains($request, '/activitypub/') ||
str_contains($request, '/api/') ||
str_contains($request, '/assets/') ||
str_contains($request, '/inbox');

You could also check for Request-Type headers.

A cookie can be faked, you should consider to store the OK in the session instead?
@Melanie Wehowski cookie could be faked but so can headers, and the way I have it now the cookie is only good for the session, bots and scrapers start a new session every time they probe a domain so no need to store, each time they troll its a new session, putting inbox in the check I guess could be useful to gate it, but when I sat down to do this and dug through all my access logs I did not have even one call for the inbox to be worried about it, now if I get bots or scrapers that start calling for my inbox I will gate it, but my understanding of how friendica and activitypub are setup the inbox should not be callable by bots and scrapers, I maybe wrong on that, I have had this instance up an running a year, and it has be en a lot of learning and trial and error so many errors, but so far this gate has really made my access logs go empty, which for me is good, im still fully federated but the datahogs have slowed or stopped trolling me
@Melanie Wehowski also if you wanted to go that far as adding /inbox following the logic you should probably also add /nodeinfo as well;
@pasjrwoctx👽 You are right, sorry I did not read the complete code, I was in $is_fediverse =... line.
if ($request === '/' makes it clear, sorry!
$is_fediverse and is_static is not necessary then?
@pasjrwoctx👽
cookie could be faked but so can headers, and the way I have it now the cookie is only good for the session, bots and scrapers start a new session every time

The sessionID is in the cookie yes, but the info can be in the session. If a bad bot starts a new session, well it will not have the "OK" in the session.
Accept headers can be faked but that would make less sense as it breaks content negotiation, and you can use it as ONE aspect of other bot indicators.

, but my understanding of how friendica and activitypub are setup the inbox should not be callable by bots and scrapers

For server to server protocol an instance will act like a "legitim bot", it will not pass a captcha or session check, validation of the request is done via signatures.
@Melanie Wehowski the other reason I elected to not have a server side stored is one privacy of actual humans that visit my site directly, and I just rather not be collecting useless data on a fairly restricted shared hosting environment, I am sure with a little time and a bit more effort on my part I could build fort knox around my instance, but the whole point of this was to cut down on the constant abusive bots and scrappers, and at this time it appears to have worked or is working as is;
@pasjrwoctx👽 to fend off bots I use anubis which I have placed in front of Friendica.