The Problem: Sharing Dashboards Securely

Teams often need to share operational dashboards with external stakeholders — suppliers, partners, or contractors. The challenge is balancing accessibility with security. You want stakeholders to view the data, but not leave it completely open to the internet.

Most collaboration platforms offer basic link sharing, but lack fine-grained access control. What if you need password protection, geographic restrictions, or brute-force prevention?

This is where Cloudflare Workers come in.

The Architecture

The solution uses a simple three-layer approach:

User → Cloudflare Worker (Security Gate) → Dashboard (Feishu/Notion/Google Sheets)

The Worker acts as a reverse proxy with authentication logic. It sits between the user and the dashboard, enforcing security policies before granting access.

Why Cloudflare Workers?

FeatureBenefit
Edge computingRuns on 300+ global edge locations, sub-50ms latency
Request metadataAccess to CF-IPCountry, CF-Connecting-IP, and other headers
Cookie supportSet and read cookies for session management
Turnstile integrationBuilt-in CAPTCHA without third-party dependencies
Free tier100,000 requests/day, more than enough for most dashboards

Implementation: The Password Gate

Step 1: Basic Password Verification

The core logic is straightforward: check for a valid cookie, verify the password, then redirect.

const DASHBOARD_URL = "https://your-dashboard-url.com";
const COOKIE_MAX_AGE = 604800; // 7 days

export default {
  async fetch(request) {
    const url = new URL(request.url);
    
    // Check existing session
    const cookies = parseCookies(request.headers.get("Cookie"));
    if (cookies.session === "verified") {
      return Response.redirect(DASHBOARD_URL, 302);
    }
    
    // Verify password submission
    if (url.searchParams.get("pwd") === PASSWORD) {
      return new Response(null, {
        status: 302,
        headers: {
          "Location": DASHBOARD_URL,
          "Set-Cookie": "session=verified; Path=/; Max-Age=" + COOKIE_MAX_AGE + "; Secure; HttpOnly; SameSite=Lax"
        }
      });
    }
    
    // Show login page
    return new Response(loginPage(), {
      headers: { "Content-Type": "text/html" }
    });
  }
};

Key technical detail: Response.redirect() returns an immutable response. You cannot add headers to it. Instead, create a new Response with status 302 and set the Location header manually.

Step 2: Country Whitelisting

Cloudflare provides the visitor country via the CF-IPCountry header. This enables geographic restrictions without any additional infrastructure.

const ALLOWED_COUNTRIES = ["CN", "HK", "TH"];

// At the start of fetch handler:
const country = request.cf?.country || "";
if (!ALLOWED_COUNTRIES.includes(country)) {
  return new Response(forbiddenPage(country), {
    status: 403,
    headers: { "Content-Type": "text/html" }
  });
}

This is particularly useful for B2B scenarios where your stakeholders are in specific regions. It blocks random internet traffic while allowing legitimate users.

Step 3: CAPTCHA with Turnstile

Cloudflare Turnstile provides bot protection without the friction of traditional CAPTCHAs. It runs invisibly in the background, only challenging suspicious traffic.

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js"></script>
<form method="post">
  <input type="password" name="pwd" placeholder="Password">
  <div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
  <button type="submit">Access</button>
</form>

Server-side verification:

async function verifyTurnstile(token, ip) {
  const response = await fetch(
    "https://challenges.cloudflare.com/turnstile/v0/siteverify",
    {
      method: "POST",
      body: new URLSearchParams({
        secret: TURNSTILE_SECRET_KEY,
        response: token,
        remoteip: ip
      })
    }
  );
  const data = await response.json();
  return data.success === true;
}

Step 4: Rate Limiting

For brute-force protection, Cloudflare offers rate limiting rules in the dashboard. Configure it under Security → WAF → Rate limiting:

SettingValue
Fieldhttp.request.uri.path
Operatorequals
Value/
Rate10 requests per 1 hour
ActionBlock

This blocks any IP that attempts more than 10 password submissions per hour. Combined with Turnstile, it makes brute-force attacks practically impossible.

The Complete Security Stack

LayerProtectionImplementation
GeographicBlock non-whitelisted countriesCF-IPCountry header check
Bot detectionPrevent automated attacksCloudflare Turnstile
AuthenticationPassword verificationCookie-based session
Rate limitingPrevent brute-forceCloudflare WAF rules
TransportEncrypted communicationHTTPS (automatic)

Lessons Learned

Always use Secure; HttpOnly; SameSite=Lax flags:

  • Secure — cookie only sent over HTTPS
  • HttpOnly — inaccessible to JavaScript (prevents XSS theft)
  • SameSite=Lax — prevents CSRF attacks

2. Session Management

A 7-day cookie balance is practical for dashboards. Users will not be repeatedly challenged, but compromised sessions expire relatively quickly.

3. Error Messages

Do not reveal whether the password is wrong or the CAPTCHA failed. Generic error messages prevent attackers from enumerating valid passwords.

4. Country Detection

The CF-IPCountry header is highly reliable but not perfect. VPN users may appear from different countries. For dashboard sharing with known stakeholders, this is usually acceptable.

When to Use This Pattern

This approach works well when:

  • You need to share dashboards with external stakeholders
  • The dashboard platform lacks built-in password protection
  • You need geographic restrictions
  • You want enterprise-grade security without enterprise costs

It works with any dashboard that supports link sharing — Feishu Bitable, Notion, Google Sheets, Grafana, or custom web applications.


What security challenges have you faced when sharing operational data with external partners?