Link Partner Board (No-DB “Backlink Exchange” That’s Not Spammy)

January 3, 2026

What this is: A tiny “link partner board” that lets people request a listing, then you manually approve it. This is a safer alternative to sketchy “backlink exchangers” because it’s curated, not automated.

What it does:

  • Public page: shows approved partners + a request form.
  • Admin mode: approve/deny requests.
  • Optional reciprocity check: fetches their “links” page and looks for your site URL/host.
  • Outbound links default to rel="nofollow" to avoid turning your site into a link scheme.

Install:

  1. Create a folder like /partners/
  2. Save the script below as index.php in that folder
  3. Edit the config at the top ($SITE_URL, $ADMIN_TOKEN)
  4. Make sure the folder is writable so it can create partners.json
  5. Visit:
  • Public: /partners/
  • Admin: /partners/?admin=YOUR_TOKEN

Note: This is meant for real resources + genuine partners. If you try to automate swaps at scale, you’re basically building a link farm. 🚫

<?php
declare(strict_types=1);

/**
 * Tiny Link Partner Board (No DB)
 * - One file + partners.json
 * - Public: approved list + request form
 * - Admin: approve/deny + optional reciprocal check
 *
 * Admin URL:
 *   /partners/?admin=YOUR_TOKEN
 */

// ---------------- CONFIG ----------------
$SITE_NAME   = 'Your Site';
$SITE_URL    = 'https://example.com/';      // your canonical URL (include https)
$ADMIN_TOKEN = 'CHANGE_ME_LONG_RANDOM';     // keep secret
$DATA_FILE   = __DIR__ . '/partners.json';

$OUT_REL     = 'nofollow noopener';         // keep nofollow by default
$MAX_DESC    = 160;

// anti-spam
$HP_FIELD    = 'companyfax';                // honeypot (must stay empty)
$MIN_SECONDS = 5;                           // user must wait this long before submit
// --------------------------------------

// ---------- helpers ----------
function h(string $s): string { return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); }

function is_valid_url(string $u): bool {
  if (!filter_var($u, FILTER_VALIDATE_URL)) return false;
  $p = parse_url($u);
  if (!$p || empty($p['scheme']) || empty($p['host'])) return false;
  $sch = strtolower((string)$p['scheme']);
  return $sch === 'http' || $sch === 'https';
}

function load_data(string $file): array {
  if (!is_file($file)) return ['requests'=>[], 'partners'=>[]];
  $raw = @file_get_contents($file);
  if ($raw === false) return ['requests'=>[], 'partners'=>[]];
  $j = json_decode($raw, true);
  if (!is_array($j)) return ['requests'=>[], 'partners'=>[]];
  $j['requests'] = (isset($j['requests']) && is_array($j['requests'])) ? $j['requests'] : [];
  $j['partners'] = (isset($j['partners']) && is_array($j['partners'])) ? $j['partners'] : [];
  return $j;
}

function save_data(string $file, array $data): bool {
  $out = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
  if ($out === false) return false;
  return @file_put_contents($file, $out, LOCK_EX) !== false;
}

function now_iso(): string { return gmdate('c'); }

function quick_fetch(string $url): ?string {
  $ctx = stream_context_create([
    'http' => [
      'timeout' => 6,
      'user_agent' => 'TinyPartnerBoard/1.0',
      'follow_location' => 1,
      'max_redirects' => 3,
    ],
  ]);
  $html = @file_get_contents($url, false, $ctx);
  if ($html === false || $html === '') return null;
  return $html;
}

function reciprocal_check(string $theirLinkPage, string $yourUrl): array {
  // returns [ok(bool|null), note(string)]
  $theirLinkPage = trim($theirLinkPage);
  if ($theirLinkPage === '') return [null, 'No link page provided'];
  if (!is_valid_url($theirLinkPage)) return [false, 'Invalid link page URL'];

  $html = quick_fetch($theirLinkPage);
  if ($html === null) return [false, 'Fetch failed'];

  $yourHost = (string)(parse_url($yourUrl, PHP_URL_HOST) ?? '');
  $ok = (stripos($html, $yourUrl) !== false) || ($yourHost !== '' && stripos($html, $yourHost) !== false);

  return [$ok, $ok ? 'Found your link' : 'No link found'];
}

// ---------- state ----------
session_start();
if (!isset($_SESSION['t_form'])) $_SESSION['t_form'] = time();

$data = load_data($DATA_FILE);
$isAdmin = isset($_GET['admin']) && hash_equals($ADMIN_TOKEN, (string)$_GET['admin']);

$notice = '';
$error  = '';

// ---------- actions ----------
if (!$isAdmin && $_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['action'] ?? '') === 'request') {
  $hp = (string)($_POST[$HP_FIELD] ?? '');
  if ($hp !== '') $error = 'Spam blocked.';
  else {
    $elapsed = time() - (int)($_SESSION['t_form'] ?? time());
    if ($elapsed < $MIN_SECONDS) $error = 'Please wait a moment and try again.';
  }

  $name     = trim((string)($_POST['name'] ?? ''));
  $site     = trim((string)($_POST['site'] ?? ''));
  $linkpage = trim((string)($_POST['link_page'] ?? ''));
  $desc     = trim((string)($_POST['desc'] ?? ''));

  if ($error === '') {
    if ($name === '' || strlen($name) > 80) $error = 'Site name is required (max 80 chars).';
    elseif (!is_valid_url($site)) $error = 'Please enter a valid site URL.';
    elseif ($linkpage !== '' && !is_valid_url($linkpage)) $error = 'Link page URL looks invalid.';
    elseif (strlen($desc) > $MAX_DESC) $error = 'Description too long.';
  }

  if ($error === '') {
    $data['requests'][] = [
      'id'        => bin2hex(random_bytes(8)),
      'name'      => $name,
      'site'      => $site,
      'link_page' => $linkpage,
      'desc'      => $desc,
      'created'   => now_iso(),
      'ip'        => $_SERVER['REMOTE_ADDR'] ?? '',
    ];
    if (save_data($DATA_FILE, $data)) {
      $notice = 'Request received — thanks! (Manual review.)';
      $_SESSION['t_form'] = time();
    } else {
      $error = 'Could not save. Check folder permissions.';
    }
  }
}

if ($isAdmin && $_SERVER['REQUEST_METHOD'] === 'POST') {
  $action = (string)($_POST['action'] ?? '');
  $id     = (string)($_POST['id'] ?? '');

  if ($action === 'approve') {
    foreach ($data['requests'] as $k => $r) {
      if (($r['id'] ?? '') === $id) {
        $data['partners'][] = [
          'id'        => $r['id'],
          'name'      => $r['name'],
          'site'      => $r['site'],
          'desc'      => $r['desc'],
          'link_page' => $r['link_page'],
          'approved'  => now_iso(),
          'rel'       => $OUT_REL,
          'check_ok'  => null,
          'check_note'=> '',
          'checked'   => '',
        ];
        unset($data['requests'][$k]);
        $data['requests'] = array_values($data['requests']);
        $notice = 'Approved.';
        break;
      }
    }
    save_data($DATA_FILE, $data);
  }

  if ($action === 'deny') {
    foreach ($data['requests'] as $k => $r) {
      if (($r['id'] ?? '') === $id) {
        unset($data['requests'][$k]);
        $data['requests'] = array_values($data['requests']);
        $notice = 'Denied/removed.';
        break;
      }
    }
    save_data($DATA_FILE, $data);
  }

  if ($action === 'check') {
    foreach ($data['partners'] as &$p) {
      if (($p['id'] ?? '') === $id) {
        [$ok, $note] = reciprocal_check((string)($p['link_page'] ?? ''), $SITE_URL);
        $p['check_ok'] = $ok;
        $p['check_note'] = $note;
        $p['checked'] = now_iso();
        $notice = 'Checked.';
        break;
      }
    }
    unset($p);
    save_data($DATA_FILE, $data);
  }
}

// sort newest first
$partners = $data['partners'];
usort($partners, fn($a,$b) => strcmp((string)($b['approved'] ?? ''), (string)($a['approved'] ?? '')));

?><!doctype html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?php echo h($SITE_NAME); ?> — Partners</title>
<style>
  body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;background:#0b0f16;color:#e8eef8}
  .wrap{max-width:860px;margin:0 auto;padding:20px}
  .card{background:#111a26;border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:14px;margin:12px 0}
  a{color:#8ad1ff}
  .muted{color:rgba(232,238,248,.7)}
  .row{display:flex;gap:10px;flex-wrap:wrap}
  input,textarea{width:100%;padding:10px;border-radius:10px;border:1px solid rgba(255,255,255,.12);background:#0c1420;color:#e8eef8}
  textarea{min-height:90px;resize:vertical}
  button{padding:10px 12px;border:0;border-radius:10px;background:#2a8cff;color:#07101b;font-weight:800;cursor:pointer}
  button.secondary{background:#243449;color:#e8eef8}
  code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
  .ok{color:#7CFF9A} .bad{color:#FF7C7C}
</style>

<div class="wrap">
  <div class="card">
    <h1 style="margin:0 0 6px;"><?php echo h($SITE_NAME); ?> Partners</h1>
    <div class="muted">Curated listings (manual approvals). Outbound links are <code>nofollow</code> by default.</div>

    <?php if ($notice): ?><div class="card" style="background:#0e2233;border-color:rgba(42,140,255,.25)"><?php echo h($notice); ?></div><?php endif; ?>
    <?php if ($error): ?><div class="card" style="background:#2a0f14;border-color:rgba(255,124,124,.25)"><?php echo h($error); ?></div><?php endif; ?>
  </div>

  <?php if (!$partners): ?>
    <div class="card"><span class="muted">No partners listed yet.</span></div>
  <?php else: ?>
    <?php foreach ($partners as $p): ?>
      <div class="card">
        <div style="display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap">
          <div>
            <strong><?php echo h((string)$p['name']); ?></strong>
            <div class="muted" style="font-size:13px;margin-top:3px">Approved: <?php echo h((string)($p['approved'] ?? '')); ?></div>
          </div>
          <div>
            <a href="<?php echo h((string)$p['site']); ?>" target="_blank" rel="<?php echo h((string)($p['rel'] ?? 'nofollow noopener')); ?>">Visit →</a>
          </div>
        </div>
        <?php if (!empty($p['desc'])): ?><div style="margin-top:8px"><?php echo h((string)$p['desc']); ?></div><?php endif; ?>
      </div>
    <?php endforeach; ?>
  <?php endif; ?>

  <?php if (!$isAdmin): ?>
    <div class="card">
      <h2 style="margin:0 0 10px;font-size:18px;">Request a listing</h2>
      <div class="muted" style="margin:0 0 10px;">Real resources only. Manual review.</div>

      <form method="post">
        <input type="hidden" name="action" value="request">

        <div class="row">
          <div style="flex:1;min-width:220px">
            <div class="muted" style="font-size:13px;margin:0 0 6px">Site name</div>
            <input name="name" required maxlength="80" autocomplete="off">
          </div>
          <div style="flex:1;min-width:260px">
            <div class="muted" style="font-size:13px;margin:0 0 6px">Your URL</div>
            <input name="site" required placeholder="https://yoursite.com/">
          </div>
        </div>

        <div style="margin-top:10px">
          <div class="muted" style="font-size:13px;margin:0 0 6px">Link page (optional)</div>
          <input name="link_page" placeholder="https://yoursite.com/links/">
        </div>

        <div style="margin-top:10px">
          <div class="muted" style="font-size:13px;margin:0 0 6px">Short description (optional)</div>
          <textarea name="desc" maxlength="<?php echo (int)$MAX_DESC; ?>" placeholder="What is your site about? (max <?php echo (int)$MAX_DESC; ?> chars)"></textarea>
        </div>

        <!-- honeypot -->
        <div style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden">
          <label>Leave blank <input name="<?php echo h($HP_FIELD); ?>"></label>
        </div>

        <div style="margin-top:12px">
          <button type="submit">Send request</button>
        </div>
      </form>
    </div>

    <div class="card">
      <strong>Optional “link back” snippet</strong>
      <div class="muted" style="margin-top:6px;">If you want to link to this page:</div>
      <pre style="white-space:pre-wrap;background:#0c1420;border:1px solid rgba(255,255,255,.10);padding:10px;border-radius:10px;margin-top:10px"><code>&lt;a href="<?php echo h($SITE_URL); ?>" rel="nofollow"&gt;<?php echo h($SITE_NAME); ?>&lt;/a&gt;</code></pre>
    </div>

  <?php else: ?>
    <div class="card">
      <h2 style="margin:0 0 10px;font-size:18px;">Admin</h2>
      <div class="muted">Requests: <?php echo count($data['requests']); ?> • Partners: <?php echo count($data['partners']); ?></div>
    </div>

    <?php if (!empty($data['requests'])): ?>
      <div class="card">
        <h3 style="margin:0 0 10px;">Pending</h3>
        <?php foreach ($data['requests'] as $r): ?>
          <div class="card" style="background:#0c1420">
            <strong><?php echo h((string)$r['name']); ?></strong>
            <div class="muted" style="font-size:13px;margin-top:4px"><?php echo h((string)$r['site']); ?></div>
            <?php if (!empty($r['link_page'])): ?>
              <div class="muted" style="font-size:13px;margin-top:4px">Link page: <?php echo h((string)$r['link_page']); ?></div>
            <?php endif; ?>
            <?php if (!empty($r['desc'])): ?><div style="margin-top:8px"><?php echo h((string)$r['desc']); ?></div><?php endif; ?>

            <form method="post" style="margin-top:10px;display:flex;gap:10px;flex-wrap:wrap">
              <input type="hidden" name="id" value="<?php echo h((string)$r['id']); ?>">
              <button name="action" value="approve">Approve</button>
              <button class="secondary" name="action" value="deny" type="submit">Deny</button>
            </form>
          </div>
        <?php endforeach; ?>
      </div>
    <?php endif; ?>

    <?php if (!empty($data['partners'])): ?>
      <div class="card">
        <h3 style="margin:0 0 10px;">Reciprocal check</h3>
        <?php foreach ($data['partners'] as $p): ?>
          <div class="card" style="background:#0c1420">
            <strong><?php echo h((string)$p['name']); ?></strong>
            <div class="muted" style="font-size:13px;margin-top:4px">Link page: <?php echo h((string)($p['link_page'] ?? '')); ?></div>
            <div class="muted" style="font-size:13px;margin-top:4px">
              Last check: <?php echo h((string)($p['checked'] ?? '')); ?> •
              Status:
              <?php
                $ok = $p['check_ok'] ?? null;
                if ($ok === true) echo '<span class="ok">OK</span>';
                elseif ($ok === false) echo '<span class="bad">Missing</span>';
                else echo '<span class="muted">—</span>';
              ?>
              <?php if (!empty($p['check_note'])) echo ' (' . h((string)$p['check_note']) . ')'; ?>
            </div>

            <form method="post" style="margin-top:10px">
              <input type="hidden" name="id" value="<?php echo h((string)$p['id']); ?>">
              <button name="action" value="check">Run check</button>
            </form>
          </div>
        <?php endforeach; ?>
      </div>
    <?php endif; ?>
  <?php endif; ?>
</div>

Comments (0)

No comments yet — be the first.

← Back to all scripts