Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungKI half beim Finden: iOS 27 schließt mehr als 100 Sicherheitslücken(21.09.2026 um 06:00 Uhr)
Sichere ProgrammierungWhat Is Rowhammer? How Can Repeated Memory Access Flip Bits in RAM?(21.09.2026 um 07:12 Uhr)
Sichere Programmierungnpm publish Ignores .gitignore: The .npmignore Override Rule(21.09.2026 um 07:15 Uhr)
Sichere ProgrammierungAphelion Editor - A free node-based video / VFX editor(21.09.2026 um 07:21 Uhr)
Sichere ProgrammierungGovernance Attack Surface Review: OKX(21.09.2026 um 07:31 Uhr)
Sichere ProgrammierungJSM Portal Request Create Property Panel Submit(21.09.2026 um 07:34 Uhr)
Reverse Engineeringsearch instructions assembly easy (X86,RISCV,AARCH64,etc)(20.09.2026 um 15:44 Uhr)
Sichere ProgrammierungKI half beim Finden: iOS 27 schließt mehr als 100 Sicherheitslücken(21.09.2026 um 06:00 Uhr)
Sichere ProgrammierungWhat Is Rowhammer? How Can Repeated Memory Access Flip Bits in RAM?(21.09.2026 um 07:12 Uhr)
Sichere Programmierungnpm publish Ignores .gitignore: The .npmignore Override Rule(21.09.2026 um 07:15 Uhr)
Sichere ProgrammierungAphelion Editor - A free node-based video / VFX editor(21.09.2026 um 07:21 Uhr)
Sichere ProgrammierungGovernance Attack Surface Review: OKX(21.09.2026 um 07:31 Uhr)
Sichere ProgrammierungJSM Portal Request Create Property Panel Submit(21.09.2026 um 07:34 Uhr)
Reverse Engineeringsearch instructions assembly easy (X86,RISCV,AARCH64,etc)(20.09.2026 um 15:44 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Building a Self-Hosted URL Shortener with Cloudflare Workers

Reagiere als Erste:r — dein Feedback zählt!

Clone the companion repo: All production code from this guide is available at IAMDevBox/cloudflare-url-shortener — worker.js, wrangler.toml, Python client, and integration tests, ready to deploy.

The Problem: Twitter's 280-Character Limit

When sharing technical blog posts on Twitter, I constantly hit the 280-character limit. Long URLs consume precious space that should be used for actual content. For example:

Full URL with UTM: 155 characters
https://iamdevbox.com/posts/building-complete-oidc-login-flow-urls/?utm_source=twitter&utm_medium=social&utm_campaign=blog_post

Available for content: Only 125 characters

This leaves barely enough room for a meaningful tweet. Third-party URL shorteners like Bitly work, but they:

  • Cost money for custom domains ($29/month for Bitly Pro)
  • Don't give you full control over your data
  • May inject their own analytics or tracking
  • Could shut down and break all your links

The Solution: Cloudflare Workers

Cloudflare Workers is a serverless platform that runs your code at the edge, across Cloudflare's global network. Combined with KV (Key-Value) storage, it's perfect for building a URL shortener.

Why Cloudflare Workers?

  • Free Tier: 100,000 requests/day
  • Global: <15ms response time worldwide
  • Custom Domain: Use your own domain
  • No Servers: Fully managed, auto-scaling
  • Simple: JavaScript/TypeScript code

Architecture Overview

The system has three main components:

flowchart TB
    A["User clicks: example.com/s/abc123"] --> B["Cloudflare Worker<br/>(Edge Network)"]
    B --> C["KV Storage<br/>Key: abc123<br/>Value: {url, campaign}"]
    C --> D["301 Redirect + UTM parameters"]

    style A fill:#667eea,color:#fff
    style B fill:#ed8936,color:#fff
    style C fill:#48bb78,color:#fff
    style D fill:#9f7aea,color:#fff

Implementation

Step 1: Worker Code

Create worker.js with the main logic:

export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    // Handle short URL redirects
    if (url.pathname.startsWith('/s/')) {
      return handleRedirect(url, env);
    }

    // Handle API: create short URL
    if (url.pathname === '/api/shorten') {
      return handleCreate(request, env);
    }

    // Handle API: get statistics
    if (url.pathname.startsWith('/api/stats/')) {
      return handleStats(url, env);
    }

    return new Response('Not Found', { status: 404 });
  }
};

async function handleRedirect(url, env) {
  const code = url.pathname.split('/s/')[1];

  // Get mapping from KV
  const mapping = await env.URL_MAPPINGS.get(code, { type: 'json' });

  if (!mapping) {
    return new Response('Short URL not found', { status: 404 });
  }

  // Build target URL with UTM parameters
  const targetUrl = new URL(mapping.url);
  const source = url.searchParams.get('s') || 'short';

  targetUrl.searchParams.set('utm_source', source);
  targetUrl.searchParams.set('utm_medium', 'shortlink');
  targetUrl.searchParams.set('utm_campaign', mapping.campaign || 'general');

  // Track stats asynchronously
  trackAccess(code, source, env);

  // 301 permanent redirect
  return Response.redirect(targetUrl.toString(), 301);
}

async function handleCreate(request, env) {
  // Verify API key
  const authHeader = request.headers.get('Authorization');
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return new Response('Unauthorized', { status: 401 });
  }

  const apiKey = authHeader.substring(7);
  if (apiKey !== env.API_KEY) {
    return new Response('Invalid API key', { status: 401 });
  }

  const body = await request.json();
  const { url: longUrl, code, campaign } = body;

  // Generate or use provided short code
  const shortCode = code || generateCode();

  // Store in KV
  await env.URL_MAPPINGS.put(shortCode, JSON.stringify({
    url: longUrl,
    campaign: campaign || 'general',
    created: new Date().toISOString()
  }));

  return Response.json({
    shortUrl: `https://example.com/s/${shortCode}`,
    code: shortCode,
    longUrl
  });
}

function generateCode() {
  const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  let code = '';
  for (let i = 0; i < 6; i++) {
    code += chars[Math.floor(Math.random() * chars.length)];
  }
  return code;
}

async function trackAccess(code, source, env) {
  const statsKey = `stats:${code}`;
  const stats = await env.URL_MAPPINGS.get(statsKey, { type: 'json' }) || {
    total: 0,
    sources: {}
  };

  stats.total++;
  stats.sources[source] = (stats.sources[source] || 0) + 1;
  stats.lastAccess = new Date().toISOString();

  await env.URL_MAPPINGS.put(statsKey, JSON.stringify(stats));
}

Step 2: Configuration

Create wrangler.toml:

name = "url-shortener"
main = "worker.js"
compatibility_date = "2024-01-01"

[[kv_namespaces]]
binding = "URL_MAPPINGS"
id = "YOUR_KV_NAMESPACE_ID"

[[routes]]
pattern = "yourdomain.com/s/*"
zone_name = "yourdomain.com"

[[routes]]
pattern = "yourdomain.com/api/shorten"
zone_name = "yourdomain.com"

[[routes]]
pattern = "yourdomain.com/api/stats/*"
zone_name = "yourdomain.com"

Step 3: Deploy

# Install Wrangler CLI
npm install -g wrangler

# Login to Cloudflare
wrangler login

# Create KV namespace
wrangler kv:namespace create URL_MAPPINGS

# Set API key (for authentication)
wrangler secret put API_KEY

# Deploy
wrangler deploy

Python Client Integration

Create a Python client for easy integration:

import os
import requests
from typing import Optional

class URLShortener:
    def __init__(self, api_key: Optional[str] = None):
        self.api_key = api_key or os.getenv('SHORTENER_API_KEY')
        self.api_endpoint = 'https://yourdomain.com/api/shorten'

    def create(self, long_url: str, code: Optional[str] = None,
               campaign: str = 'blog_post') -> Optional[str]:
        if not self.api_key:
            return None

        try:
            response = requests.post(
                self.api_endpoint,
                json={
                    'url': long_url,
                    'code': code,
                    'campaign': campaign
                },
                headers={'Authorization': f'Bearer {self.api_key}'},
                timeout=10
            )

            if response.status_code == 200:
                return response.json()['shortUrl']
        except Exception as e:
            print(f'Error creating short URL: {e}')

        return None

def shorten_url_for_twitter(long_url: str) -> str:
    """Create short URL with automatic fallback"""
    shortener = URLShortener()
    short_url = shortener.create(long_url)

    # Fallback to simplified UTM if shortening fails
    return short_url if short_url else f'{long_url}?utm_source=twitter'

Usage Example

from url_shortener import shorten_url_for_twitter

# Original URL (115 characters)
url = 'https://example.com/posts/building-self-hosted-url-shortener-cloudflare-workers/'

# Shortened URL (32 characters)
short = shorten_url_for_twitter(url)
# Result: https://example.com/s/abc123

# Character savings: 83 characters (72% reduction)
print(f'Saved {len(url) - len(short)} characters!')

Results

Before and after comparison for Twitter:

URL Type Length Available for Content
Full URL + UTM 155 chars 125 chars (44%)
Short URL 32 chars 248 chars (88%)

98% increase in available space for content!

GitHub Actions Integration

Automate short URL creation in your CI/CD pipeline:

name: Auto Publish

on:
  push:
    branches: [main]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup URL Shortener
        run: |
          echo "SHORTENER_API_KEY=${{ secrets.SHORTENER_API_KEY }}" >> $GITHUB_ENV

      - name: Publish to Twitter
        run: |
          python3 publish.py --use-short-urls

Cost Analysis

Cloudflare Workers Free Tier:

  • 100,000 requests/day
  • 1GB KV storage
  • Unlimited bandwidth

Estimated usage for a blog:

  • ~500 short URL clicks/day
  • ~5 new URLs created/day
  • ~1MB KV storage used

Cost: $0/month (well within free tier)

Comparison:

  • Bitly Pro: $29/month
  • TinyURL Pro: $9.99/month
  • Self-hosted: $0/month ✨

Performance

Tested from different locations:

San Francisco  → <10ms
Shanghai       → <15ms
London         → <12ms
Tokyo          → <8ms
Singapore      → <10ms

Thanks to Cloudflare's edge network, responses are blazing fast worldwide.

Security Considerations

  1. API Authentication: Creating short URLs requires API key
  2. Public Access: Redirects work without authentication
  3. Anonymous Stats: No IP addresses or personal data stored
  4. Rate Limiting: Built-in via Cloudflare

Monitoring

View real-time metrics in Cloudflare Dashboard:

  • Request count
  • Error rate
  • P50/P95/P99 latency
  • KV read/write operations

Advanced Features

Custom Short Codes

Create memorable short URLs:

shortener.create(
    long_url='https://example.com/posts/oauth-guide/',
    code='oauth'  # Custom code
)
# Result: https://example.com/s/oauth

Click Analytics

Track where your clicks come from:

curl https://example.com/api/stats/abc123

Response:

{
  "total": 142,
  "sources": {
    "twitter": 98,
    "linkedin": 32,
    "direct": 12
  },
  "lastAccess": "2025-11-27T10:30:00Z"
}

🎯 Key Takeaways

  • Cost money for custom domains ($29/month for Bitly Pro)
  • Don't give you full control over your data
  • May inject their own analytics or tracking

Conclusion

Building a self-hosted URL shortener with Cloudflare Workers provides:

Complete control over your URLs
Zero cost (free tier is generous)
Global performance (<15ms worldwide)
Custom domain (your brand)
Privacy (no third-party tracking)
Reliability (99.99% uptime SLA)

For Twitter and other character-limited platforms, this solution provides 98% more space for content compared to full URLs.

The complete implementation takes under 5 minutes to deploy and is production-ready. Perfect for developers, bloggers, and anyone sharing links on social media.

Resources

Happy link shortening! 🚀

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Building a Self-Hosted URL Shortener with Cloudflare Workers

Thematisch verwandte Begriffe: Building, SelfHosted, Shortener, with · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-94109 | openEQUELLA versions before 2026.1.0 contain a remote code execution vul…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick