Skip to main content

Vercel / Next.js Proxy

Proxy Ovyxa requests through your Next.js application hosted on Vercel (or any platform) using rewrites or API routes.

Use Next.js rewrites to proxy analytics requests transparently.

Configuration

Add rewrites to your next.config.js:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
async rewrites() {
return [
{
source: '/stats/:path*',
destination: 'https://api.ovyxa.com/:path*'
}
]
}
}

module.exports = nextConfig

Update Script Tag

<script async
src="https://ingest.ovyxa.com/js/script.js"
data-domain="YOUR_DOMAIN"
data-api="https://yourdomain.com/stats">
</script>

Deploy and Test

  1. Deploy to Vercel: vercel --prod
  2. Visit your site and check DevTools → Network
  3. Should see requests to yourdomain.com/stats/e
  4. Events appear in dashboard within 5 minutes

Benefits:

  • Simple configuration
  • No additional code
  • Automatic HTTPS

Method 2: API Route (Advanced)

For more control (logging, rate limiting), use a Next.js API route.

Create API Route

Create /pages/api/stats/[...slug].js (Pages Router) or /app/api/stats/[...slug]/route.ts (App Router):

Pages Router:

// pages/api/stats/[...slug].js
export default async function handler(req, res) {
const slug = req.query.slug ? req.query.slug.join('/') : ''
const targetUrl = `https://api.ovyxa.com/${slug}`

try {
const response = await fetch(targetUrl, {
method: req.method,
headers: {
'Content-Type': 'application/json',
'User-Agent': req.headers['user-agent'] || '',
'X-Forwarded-For': req.headers['x-forwarded-for'] || req.socket.remoteAddress
},
body: req.method === 'POST' ? JSON.stringify(req.body) : undefined
})

const data = await response.text()
res.status(response.status).send(data)
} catch (error) {
console.error('Proxy error:', error)
res.status(500).json({ error: 'Proxy failed' })
}
}

App Router:

// app/api/stats/[...slug]/route.ts
import { NextRequest, NextResponse } from 'next/server'

export async function POST(
request: NextRequest,
{ params }: { params: { slug: string[] } }
) {
const slug = params.slug.join('/')
const targetUrl = `https://api.ovyxa.com/${slug}`

try {
const body = await request.text()

const response = await fetch(targetUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': request.headers.get('user-agent') || '',
'X-Forwarded-For': request.headers.get('x-forwarded-for') || request.ip || ''
},
body
})

const data = await response.text()
return new NextResponse(data, { status: response.status })
} catch (error) {
console.error('Proxy error:', error)
return NextResponse.json({ error: 'Proxy failed' }, { status: 500 })
}
}

// Also support GET for health checks
export async function GET(
request: NextRequest,
{ params }: { params: { slug: string[] } }
) {
const slug = params.slug.join('/')
const targetUrl = `https://api.ovyxa.com/${slug}`

const response = await fetch(targetUrl, {
method: 'GET',
headers: {
'User-Agent': request.headers.get('user-agent') || ''
}
})

const data = await response.text()
return new NextResponse(data, { status: response.status })
}

Update Script Tag

Same as Method 1:

<script async
src="https://ingest.ovyxa.com/js/script.js"
data-domain="YOUR_DOMAIN"
data-api="https://yourdomain.com/stats">
</script>

Using Script Component (Next.js)

For cleaner integration in Next.js:

// components/Analytics.jsx
import Script from 'next/script'

export default function Analytics() {
return (
<Script
src="https://ingest.ovyxa.com/js/script.js"
data-domain={process.env.NEXT_PUBLIC_OVYXA_DOMAIN}
data-api="https://yourdomain.com/stats"
strategy="afterInteractive"
/>
)
}

Then in your layout:

// app/layout.js
import Analytics from '@/components/Analytics'

export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
)
}

Environment Variables

Store your site key securely:

# .env.local
NEXT_PUBLIC_OVYXA_DOMAIN=yourdomain.com
<script
src="https://ingest.ovyxa.com/js/script.js"
data-domain={process.env.NEXT_PUBLIC_OVYXA_DOMAIN}
data-api="/stats">
</script>

Advanced: Rate Limiting with API Route

Add rate limiting using Vercel Edge Config or Upstash:

// app/api/stats/[...slug]/route.ts with rate limiting
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'

const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(100, '1 m'), // 100 req/min
analytics: true
})

export async function POST(request: NextRequest, { params }) {
const ip = request.ip ?? '127.0.0.1'
const { success } = await ratelimit.limit(ip)

if (!success) {
return NextResponse.json({ error: 'Rate limit exceeded' }, { status: 429 })
}

// Continue with proxy logic...
}

Middleware Approach (Alternative)

Use Next.js middleware for even cleaner proxying:

// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/stats')) {
const targetPath = request.nextUrl.pathname.replace('/stats', '')
const targetUrl = `https://api.ovyxa.com${targetPath}${request.nextUrl.search}`

return NextResponse.rewrite(new URL(targetUrl))
}
}

export const config = {
matcher: '/stats/:path*'
}

Troubleshooting

308 Permanent Redirect Errors

If you see 308 redirects, ensure your route doesn't conflict with static files:

  • Use /stats prefix (not /api/analytics if that's used elsewhere)
  • Check next.config.js for conflicting redirects

Body Parsing Issues

Next.js may parse body automatically. To forward raw body:

// pages/api/stats/[...slug].js
export const config = {
api: {
bodyParser: false // Disable body parsing
}
}

export default async function handler(req, res) {
const body = await new Promise((resolve) => {
let data = ''
req.on('data', chunk => { data += chunk })
req.on('end', () => resolve(data))
})

// Forward raw body...
}

Headers Not Forwarded

Ensure you forward critical headers:

  • User-Agent: For browser/device detection
  • X-Forwarded-For: For IP-based geolocation
  • Referer: For referrer tracking

Performance Considerations

Caching

Don't cache analytics requests:

// next.config.js
async headers() {
return [
{
source: '/stats/:path*',
headers: [
{ key: 'Cache-Control', value: 'no-store, no-cache, must-revalidate' }
]
}
]
}

Edge Runtime

For lower latency, use Edge runtime:

// app/api/stats/[...slug]/route.ts
export const runtime = 'edge' // Enable Edge runtime

export async function POST(request: NextRequest, { params }) {
// Proxy logic runs on Edge (faster globally)
}

Deployment Platforms

This approach works on:

  • Vercel (recommended, zero config)
  • Netlify (use _redirects file)
  • AWS Amplify (use rewrites in amplify.yml)
  • Cloudflare Pages (use _redirects)
  • Self-hosted Next.js (Node.js server)

Cost

  • Vercel Free tier: 100GB bandwidth/month
  • Vercel Pro: $20/month for 1TB bandwidth

Analytics requests are tiny (less than 1KB), so:

  • 1 million pageviews ≈ 1GB bandwidth
  • Free tier handles 100M+ pageviews/month

Learn More

Using Next.js rewrites is the simplest, most performant way to proxy analytics.