<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>redirect.name: DNS-based redirects</title>
  <style>
    :root {
      --font: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
      --mono: ui-monospace, "Cascadia Code", Menlo, monospace;
      --max-width: 680px;
      --accent: #4338ca;
      --bg: #ffffff;
      --fg: #111111;
      --muted: #555555;
      --soft: #f4f4f7;
      --inline: rgba(15, 15, 20, 0.06);
      --border: #e5e5ec;
      --alert-bg: #fff1f0;
      --alert-border: #ffa39e;
      --alert-fg: #5a1a1a;
    }

    @media (prefers-color-scheme: dark) {
      :root {
        --accent: #818cf8;
        --bg: #0f0f14;
        --fg: #ececef;
        --muted: #9a9aa3;
        --soft: #1c1c22;
        --inline: rgba(255, 255, 255, 0.08);
        --border: #28282f;
        --alert-bg: #2a1418;
        --alert-border: #6b2a31;
        --alert-fg: #f8d7da;
      }
    }

    *, *::before, *::after { box-sizing: border-box; }

    body {
      font-family: var(--font);
      background: var(--bg);
      color: var(--fg);
      margin: 0;
      padding: 3rem 1.25rem 5rem;
      line-height: 1.6;
      font-size: 1rem;
      -webkit-font-smoothing: antialiased;
    }

    .container {
      max-width: var(--max-width);
      margin: 0 auto;
    }

    h1 {
      font-size: 2.25rem;
      font-weight: 700;
      margin: 0 0 0.5rem;
      letter-spacing: -0.03em;
    }

    h2 {
      font-size: 1.25rem;
      font-weight: 600;
      margin: 3rem 0 0.75rem;
      letter-spacing: -0.015em;
    }

    p { margin: 0 0 1rem; }

    a {
      color: var(--accent);
      text-decoration: none;
    }
    a:hover { text-decoration: underline; }

    code, pre {
      font-family: var(--mono);
      font-size: 0.875em;
    }

    code {
      background: var(--inline);
      border-radius: 4px;
      padding: 0.1em 0.35em;
    }

    pre {
      background: var(--soft);
      border-radius: 8px;
      padding: 1rem 1.25rem;
      overflow-x: auto;
      margin: 0 0 1rem;
      line-height: 1.55;
    }

    pre code {
      background: none;
      padding: 0;
      font-size: 0.875rem;
    }

    ol, ul {
      padding-left: 1.5rem;
      margin: 0 0 1rem;
    }

    li { margin-bottom: 0.4rem; }

    table {
      display: block;
      width: 100%;
      border-collapse: collapse;
      font-size: 0.9rem;
      margin: 0 0 1rem;
      overflow-x: auto;
    }

    th {
      text-align: left;
      font-weight: 600;
      border-bottom: 1px solid var(--border);
      padding: 0.5rem 0.75rem;
    }

    td {
      padding: 0.5rem 0.75rem;
      border-bottom: 1px solid var(--border);
      vertical-align: top;
    }

    tr:last-child td { border-bottom: none; }

    table code {
      background: none;
      padding: 0;
    }

    .dns-record th:first-child,
    .dns-record td:first-child {
      width: 4.5rem;
    }

    .tagline {
      color: var(--muted);
      margin: 0 0 2.5rem;
      font-size: 1.125rem;
    }

    .alert {
      background: var(--alert-bg);
      border: 1px solid var(--alert-border);
      color: var(--alert-fg);
      border-radius: 6px;
      padding: 0.75rem 1rem;
      margin: 0 0 1.5rem;
      font-size: 0.95rem;
      display: none;
    }

    .alert.visible { display: block; }

    .note {
      margin-top: 0.5rem;
      font-size: 0.9rem;
      color: var(--muted);
    }

    .footer {
      margin-top: 4rem;
      font-size: 0.875rem;
      color: var(--muted);
    }

    .nb {
      white-space: nowrap;
    }
  </style>
</head>
<body>
<div class="container">

  <div id="error-alert" class="alert"></div>

  <h1>redirect.name</h1>
  <p class="tagline">DNS-based URL redirects. No server, no signup, no dashboard.</p>

  <h2>How it works</h2>
  <p>
    Two DNS records describe a redirect: one points your hostname at our server,
    and a <code>TXT</code> record describes where to send visitors. We read the
    <code>TXT</code> record at request time and issue the redirect. No accounts,
    no dashboard. Your DNS provider is the source of truth.
  </p>

  <h2>Setup</h2>
  <ol>
    <li>
      Point your hostname at <code>alias.redirect.name</code>:
      <table class="dns-record">
        <thead><tr><th>Type</th><th>Name</th><th>Value</th></tr></thead>
        <tbody>
          <tr>
            <td><code>CNAME</code></td>
            <td><code><span class="nb">go.example.com.</span></code></td>
            <td><code><span class="nb">alias.redirect.name.</span></code></td>
          </tr>
        </tbody>
      </table>
      <p class="note">
        If your DNS provider doesn't support <code>CNAME</code> flattening at the
        apex (sometimes offered as an <code>ALIAS</code> or <code>ANAME</code>
        record), you can use an <code>A</code> record pointing to
        <code>45.55.126.223</code> instead.
      </p>
    </li>
    <li>
      Add a <code>TXT</code> record at <code>_redirect.&lt;your-hostname&gt;</code>:
      <table class="dns-record">
        <thead><tr><th>Type</th><th>Name</th><th>Value</th></tr></thead>
        <tbody>
          <tr>
            <td><code>TXT</code></td>
            <td><code><span class="nb">_redirect.go.example.com.</span></code></td>
            <td><code>Redirects to <span class="nb">https://example.com/</span></code></td>
          </tr>
        </tbody>
      </table>
      <p class="note">
        The <code>_redirect.</code> prefix is required because <code>TXT</code>
        and <code>CNAME</code> records can't coexist at the same name.
      </p>
    </li>
    <li>Wait for DNS to propagate (usually a minute or two), then visit your hostname.</li>
  </ol>

  <h2>Redirect rules</h2>
  <p>The <code>TXT</code> record value is the rule. The syntax is plain English:</p>

  <table>
    <thead>
      <tr>
        <th><code>TXT</code> record value</th>
        <th>Behavior</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><code>Redirects to &lt;url&gt;</code></td>
        <td><code>302</code> to <code>&lt;url&gt;</code></td>
      </tr>
      <tr>
        <td><code>Redirects permanently to &lt;url&gt;</code></td>
        <td><code>301</code> to <code>&lt;url&gt;</code></td>
      </tr>
      <tr>
        <td><code>Redirects from &lt;path&gt; to &lt;url&gt;</code></td>
        <td><code>302</code> when the request path matches</td>
      </tr>
      <tr>
        <td><code>Redirects from &lt;path&gt; to &lt;url&gt; with 308</code></td>
        <td>Same, with explicit status: <code>301</code>, <code>302</code>, <code>307</code>, or <code>308</code></td>
      </tr>
      <tr>
        <td><code>Redirects from <span class="nb">/path/*</span> to <span class="nb">https://example.com/*</span></code></td>
        <td>Wildcard: whatever <code>*</code> matches in the path replaces <code>*</code> in the destination</td>
      </tr>
    </tbody>
  </table>

  <p>
    Destinations can use <code>http://</code>, <code>https://</code>,
    <code>ftp://</code>, <code>mailto:</code>, <code>magnet:</code>, or an
    absolute path.
  </p>

  <p>
    You can add multiple <code>TXT</code> records at the same name.
    Path-specific rules (<code>Redirects from …</code>) are tried first;
    catch-all rules (<code>Redirects to …</code>) only apply if no path rule
    matches.
  </p>

  <h2>Examples</h2>

  <p><strong>Simple redirect.</strong> Send all traffic to a single URL:</p>
  <table class="dns-record">
    <thead><tr><th>Type</th><th>Name</th><th>Value</th></tr></thead>
    <tbody>
      <tr>
        <td><code>TXT</code></td>
        <td><code><span class="nb">_redirect.go.example.com.</span></code></td>
        <td><code>Redirects to <span class="nb">https://www.example.com/</span></code></td>
      </tr>
    </tbody>
  </table>

  <p><strong>Permanent redirect.</strong> For moves that won't be undone:</p>
  <table class="dns-record">
    <thead><tr><th>Type</th><th>Name</th><th>Value</th></tr></thead>
    <tbody>
      <tr>
        <td><code>TXT</code></td>
        <td><code><span class="nb">_redirect.old.example.com.</span></code></td>
        <td><code>Redirects permanently to <span class="nb">https://new.example.com/</span></code></td>
      </tr>
    </tbody>
  </table>

  <p><strong>Path routing.</strong> Redirect one path, send the rest somewhere else:</p>
  <table class="dns-record">
    <thead><tr><th>Type</th><th>Name</th><th>Value</th></tr></thead>
    <tbody>
      <tr>
        <td><code>TXT</code></td>
        <td><code><span class="nb">_redirect.docs.example.com.</span></code></td>
        <td><code>Redirects from <span class="nb">/api</span> to <span class="nb">https://api-docs.example.com/</span></code></td>
      </tr>
      <tr>
        <td><code>TXT</code></td>
        <td><code><span class="nb">_redirect.docs.example.com.</span></code></td>
        <td><code>Redirects to <span class="nb">https://docs.example.com/</span></code></td>
      </tr>
    </tbody>
  </table>

  <p><strong>Wildcard forwarding.</strong> Whatever <code>*</code> matches in the path replaces <code>*</code> in the destination:</p>
  <table class="dns-record">
    <thead><tr><th>Type</th><th>Name</th><th>Value</th></tr></thead>
    <tbody>
      <tr>
        <td><code>TXT</code></td>
        <td><code><span class="nb">_redirect.links.example.com.</span></code></td>
        <td><code>Redirects from <span class="nb">/blog/*</span> to <span class="nb">https://blog.example.com/posts/*</span></code></td>
      </tr>
    </tbody>
  </table>

  <p><strong>Other schemes.</strong> Redirect to mail, magnet links, etc.:</p>
  <table class="dns-record">
    <thead><tr><th>Type</th><th>Name</th><th>Value</th></tr></thead>
    <tbody>
      <tr>
        <td><code>TXT</code></td>
        <td><code><span class="nb">_redirect.hi.example.com.</span></code></td>
        <td><code>Redirects to <span class="nb">mailto:hello@example.com</span></code></td>
      </tr>
    </tbody>
  </table>

  <h2>HTTPS</h2>
  <p>
    HTTPS works out of the box. Certificates are issued automatically by
    Let's Encrypt on the first request to a new hostname. That request may
    take a couple of seconds while the cert is provisioned; everything after
    is immediate.
  </p>

  <h2>Verifying your setup</h2>
  <p>If something isn't working, check what DNS is actually serving.</p>

  <p>Confirm the <code>CNAME</code> or <code>A</code> record resolves:</p>
  <pre><code>dig +short go.example.com.</code></pre>

  <p>Confirm the <code>TXT</code> record is reachable and matches what you set:</p>
  <pre><code>dig +short TXT _redirect.go.example.com.</code></pre>

  <p>
    If both look right but the redirect doesn't happen, the rule may not
    parse. Make sure the value starts with the word <code>Redirects</code>
    (capitalized) and that the destination is a fully qualified URL or an
    absolute path.
  </p>

  <p class="footer">
    <a href="https://github.com/frolic/redirect.name">View source</a>
  </p>

</div>

<script>
  (function() {
    var hash = location.hash;
    if (!hash) return;
    var match = hash.match(/[#&]reason=([^&]*)/);
    if (!match) return;
    var reason = decodeURIComponent(match[1].replace(/\+/g, ' '));
    var escaped = reason.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
    var el = document.getElementById('error-alert');
    el.innerHTML = '<strong>Error:</strong> ' + escaped;
    el.classList.add('visible');
  })();
</script>
</body>
</html>
