Vercel / Next.js Proxy
Proxy Ovyxa requests through your Next.js application hosted on Vercel (or any platform) using rewrites or API routes.
Method 1: Rewrites (Recommended)
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
- Deploy to Vercel:
vercel --prod - Visit your site and check DevTools → Network
- Should see requests to
yourdomain.com/stats/e - 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
/statsprefix (not/api/analyticsif that's used elsewhere) - Check
next.config.jsfor 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 detectionX-Forwarded-For: For IP-based geolocationReferer: 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
_redirectsfile) - 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
- Next.js Rewrites Docs
- Cloudflare Proxy - Alternative for non-Next.js sites
- Nginx Proxy - For self-hosted setups
Using Next.js rewrites is the simplest, most performant way to proxy analytics.