~/personal-dashboard — Deployment & Security
Architecture Overview
High-Level Architecture
Public Traffic
┌─────────────┐ ┌─────────────────────────┐ ┌────────────────────┐ │ Browser │ ──▶ │ Cloudflare Zero Trust │ ──▶ │ Vercel (Next.js) │ │ (Public) │ │ │ │ │ │ │ │ • Access authentication │ │ • proxy.ts │ │ │ │ • Origin secret header │ │ • Origin validation│ └─────────────┘ └─────────────────────────┘ └────────────────────┘
Local Traffic
┌─────────────┐ │ Browser │ ──▶ Vercel directly │ (Local) │ (localhost / LAN / Tailscale) │ │ │ │ proxy.ts bypasses authentication └─────────────┘
Security Layers
The application uses two independent layers of protection:
Cloudflare Zero Trust
Authenticates visitors before requests ever reach Vercel.
Prevents unauthorized users from accessing the application.
Origin Secret Verification (proxy.ts)
Cryptographically verifies that requests actually passed through Cloudflare.
Prevents direct access to the Vercel deployment.
Blocks forged Host headers and header spoofing attacks.
Domain & DNS
Purpose
Domain
Target
Public application
home.parthjain.tech
Cloudflare DNS → Vercel
Default Vercel deployment
personal-dashboard-*.vercel.app
Redirected by proxy.ts
Local development
localhost:3000
Direct access
[!IMPORTANT] The DNS record for home.parthjain.tech must be proxied (orange cloud enabled) so Cloudflare Zero Trust policies are enforced.
Cloudflare Zero Trust Configuration
Access Application
Create a Self-hosted Access application.
Setting
Value
Type
Self-hosted
Domain
home.parthjain.tech
Access Policy
Allow only your identity (email, One-Time PIN, etc.)
Transform Rule — Origin Secret Injection
This rule prevents attackers from bypassing Cloudflare and connecting directly to Vercel.
Location
Cloudflare Dashboard └── parthjain.tech └── Rules └── Transform Rules └── Modify Request Header
Rule
Setting
Value
Rule Name
Inject Vercel Origin Secret
Condition
Hostname equals home.parthjain.tech
Action
Set static request header
Header
Header
Value
X-Vercel-Origin-Secret
Same value as X_VERCEL_ORIGIN_SECRET in Vercel
[!IMPORTANT] Cloudflare does not allow custom headers beginning with cf-.
Use X-Vercel-Origin-Secret instead.
proxy.ts accepts both hyphenated and underscored header names for compatibility.
Vercel Configuration
Production Environment Variables
Variable
Purpose
DATABASE_URL
PostgreSQL connection string
INTERVALS_API_KEY
Intervals.icu API key
INTERVALS_ATHLETE_ID
Athlete ID (0 = authenticated user)
X_VERCEL_ORIGIN_SECRET
Shared origin verification secret
CF_ACCESS_CLIENT_ID
(Optional) Cloudflare Access service token
CF_ACCESS_CLIENT_SECRET
(Optional) Cloudflare Access service token secret
UPTIME_KUMA_URL
Prometheus endpoint
UPTIME_KUMA_API_KEY
API key (used as HTTP Basic password)
TELEMETRY_URL
Host telemetry endpoint
Deployment Protection
Vercel's built-in production deployment protection requires:
Vercel Pro
Advanced Deployment Protection (~$150/month add-on)
On the Hobby plan it only protects Preview Deployments, not Production.
Because of this limitation, Cloudflare Zero Trust is used instead, providing enterprise-grade authentication at no additional cost.
proxy.ts
Location
project-root/ └── proxy.ts
[!NOTE] Next.js 16 renamed middleware.ts to proxy.ts.
The exported function is proxy(), not middleware().
Request Flow
Incoming Request │ ▼
┌──────────────────────────────────────────────────────────────┐ │ Is the hostname local? │ │ │ │ localhost │ │ 127.0.0.1 │ │ *.local │ │ LAN IPs │ │ Tailscale │ │ Device hostnames │ │ │ │ YES → Allow request │ └──────────────────────────────────────────────────────────────┘ │ NO ▼
┌──────────────────────────────────────────────────────────────┐ │ Is hostname home.parthjain.tech? │ │ │ │ NO → Redirect (307) │ │ https://home.parthjain.tech/... │ └──────────────────────────────────────────────────────────────┘ │ YES ▼
┌──────────────────────────────────────────────────────────────┐ │ Is an origin secret configured? │ │ │ │ NO → Allow request │ │ (graceful setup/development mode) │ │ │ │ YES → Validate request header │ │ │ │ Match → Allow │ │ Mismatch → 403 Forbidden │ └──────────────────────────────────────────────────────────────┘
Accepted Request Headers
Checked in this order:
X-Vercel-Origin-Secret
X_Vercel_Origin_Secret
X-Origin-Secret
X_Origin_Secret
X-CF-Origin-Secret
Accepted Environment Variables
Checked in this order:
X_VERCEL_ORIGIN_SECRET
VERCEL_ORIGIN_SECRET
ORIGIN_SECRET
CF_ACCESS_ORIGIN_SECRET
Matcher
Runs on every request except static assets.
Excluded paths include:
_next/static _next/image favicon.ico
.css .js .svg .png .jpg .jpeg .gif .webp *.ico
Attack Scenarios
Scenario
Result
Visitor opens home.parthjain.tech
Cloudflare Zero Trust login
Visitor discovers .vercel.app URL
Redirected to canonical domain
Attacker spoofs Host: home.parthjain.tech
Origin secret validation fails → 403
Attacker spoofs host + secret header
Secret is unknown → 403
Bot or crawler accesses deployment
Redirect or 403
Access from localhost / LAN / Tailscale
Allowed immediately
Rotating the Origin Secret
If the shared secret is ever compromised:
Generate a new random secret.
Update both locations:
Vercel
X_VERCEL_ORIGIN_SECRET
Cloudflare Transform Rule
X-Vercel-Origin-Secret
Redeploy the Vercel application so the updated environment variable takes effect.
Security Summary
Layer
Purpose
Cloudflare Zero Trust
User authentication at the edge
Transform Rule
Injects trusted origin header
proxy.ts
Validates origin secret
Canonical Redirect
Prevents direct .vercel.app usage
Local Bypass
Preserves localhost/LAN/Tailscale development workflow