Skip to main content
The Media Proxy is Fluxer’s specialized service for processing, transforming, and serving media content. It handles image resizing, video thumbnails, format conversion, and content moderation.

Service Overview

The media proxy provides:

Image Processing

Resize, crop, and convert images to optimal formats

Video Thumbnails

Extract frames from videos for previews

Format Conversion

Convert between WebP, PNG, JPEG, and GIF

NSFW Detection

Detect and flag explicit content

Architecture

The media proxy is built with:
  • Hono - High-performance web framework
  • Sharp - Fast image processing
  • FFmpeg - Video thumbnail extraction
  • S3 - Media storage backend
  • Cloudflare - Edge caching and firewall

Image Processing

Request Format

GET /proxy/image/{hash}?width=800&height=600&format=webp&quality=80
Path Parameters:
hash
string
required
SHA-256 hash of the image
Query Parameters:
width
integer
Target width in pixels (max: 4096)
height
integer
Target height in pixels (max: 4096)
format
string
Output format: webp, png, jpeg, gif
quality
string
Quality level: low, medium, high, lossless
animated
boolean
Preserve animation (for GIF/WebP)

Quality Settings

QualityWebPJPEGPNG
low6060Compression 8
medium7575Compression 6
high9090Compression 4
lossless100100Compression 0

Example Requests

curl "https://cdn.fluxer.app/proxy/image/abc123?width=800&height=600"

Video Thumbnails

Extract Frame

GET /proxy/thumbnail/{hash}?width=1280&height=720&format=jpeg
Path Parameters:
hash
string
required
SHA-256 hash of the video
Query Parameters:
width
integer
Thumbnail width (max: 1920)
height
integer
Thumbnail height (max: 1080)
format
string
Output format: jpeg, png, webp
quality
string
Quality level (default: medium)
timestamp
number
Timestamp in seconds to extract frame from

Example

curl "https://cdn.fluxer.app/proxy/thumbnail/def456?width=640&height=360&timestamp=5"

External Media

Proxy external URLs through Fluxer:
GET /proxy/external?url={encoded_url}&width=800
url
string
required
URL-encoded external image URL
Example:
const url = encodeURIComponent('https://example.com/image.png');
const proxied = `https://cdn.fluxer.app/proxy/external?url=${url}&width=800`;
External media is cached for 24 hours. Excessive requests may be rate limited.

NSFW Detection

Detection Service

The media proxy includes optional NSFW detection:
interface NSFWDetectionResult {
  isNSFW: boolean;
  confidence: number;
  labels: Array<{
    name: string;
    confidence: number;
  }>;
}

Configuration

export interface MediaProxyConfig {
  nsfwModelPath?: string;  // Path to NSFW detection model
  // ...
}
NSFW detection requires a trained model file. This feature is optional and disabled by default.

Metadata Extraction

Get Image Metadata

GET /proxy/metadata/{hash}
Response:
{
  "width": 1920,
  "height": 1080,
  "format": "png",
  "size": 245678,
  "has_alpha": true,
  "is_animated": false,
  "frame_count": 1,
  "mime_type": "image/png"
}

Supported Formats

Input Formats

  • Images: JPEG, PNG, WebP, GIF, BMP, TIFF, SVG
  • Videos: MP4, WebM, MOV, AVI (for thumbnails)

Output Formats

  • WebP - Best compression and quality
  • PNG - Lossless with transparency
  • JPEG - Wide compatibility
  • GIF - Animation support

Authentication

Media proxy endpoints require authentication:
GET /proxy/image/abc123
Authorization: Bearer {token}
Or via signed URLs:
GET /proxy/image/abc123?signature={hmac_signature}&expires={timestamp}

Generating Signed URLs

import crypto from 'crypto';

function generateSignedUrl(
  path: string,
  secretKey: string,
  expiresIn: number = 3600
): string {
  const expires = Math.floor(Date.now() / 1000) + expiresIn;
  const message = `${path}${expires}`;
  const signature = crypto
    .createHmac('sha256', secretKey)
    .update(message)
    .digest('base64url');
  
  return `${path}?signature=${signature}&expires=${expires}`;
}

const url = generateSignedUrl(
  '/proxy/image/abc123?width=800',
  process.env.MEDIA_PROXY_SECRET
);

Caching

The media proxy implements multi-layer caching:
1

Cloudflare Edge Cache

Cached at edge locations (TTL: 7 days)
2

In-Memory Cache

Recent transformations cached in memory
3

S3 Storage

Original media stored in S3

Cache Headers

HTTP/1.1 200 OK
Cache-Control: public, max-age=604800
ETag: "abc123def456"
Last-Modified: Mon, 15 Jan 2024 10:00:00 GMT

Rate Limiting

Media proxy has specific rate limits:
OperationLimitWindow
Image Processing1001 minute
Video Thumbnails201 minute
External Media301 minute
Metadata2001 minute

Error Handling

Common Errors

{
  "code": "NOT_FOUND",
  "message": "Media not found"
}
HTTP Status: 404
{
  "code": "BAD_REQUEST",
  "message": "Invalid width parameter"
}
HTTP Status: 400
{
  "code": "PROCESSING_FAILED",
  "message": "Failed to process image"
}
HTTP Status: 500
{
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "retry_after": 30
}
HTTP Status: 429

Configuration

export interface MediaProxyConfig {
  nodeEnv: 'development' | 'production';
  secretKey: string;                    // HMAC secret for signed URLs
  requireCloudflareEdge: boolean;       // Require Cloudflare edge
  staticMode: boolean;                  // Serve static files only
  s3: S3Config;                        // S3 configuration
  nsfwModelPath?: string;              // NSFW model path
}

export interface S3Config {
  endpoint: string;                     // S3 endpoint URL
  region: string;                       // S3 region
  accessKeyId: string;                  // S3 access key
  secretAccessKey: string;              // S3 secret key
  bucketCdn: string;                    // CDN bucket name
  bucketUploads: string;                // Uploads bucket name
  bucketStatic?: string;                // Static assets bucket
}

Cloudflare Integration

Edge Firewall

The media proxy includes Cloudflare edge detection:
import { CloudflareFirewall } from '@fluxer/media_proxy';

app.use('*', CloudflareFirewall(config));
This middleware:
  • Validates requests come from Cloudflare edge
  • Blocks direct access when requireCloudflareEdge is enabled
  • Extracts real client IP from CF-Connecting-IP

Edge Caching

Configure Cloudflare caching:
// Cloudflare Page Rule
Cache Level: Cache Everything
Edge Cache TTL: 7 days
Browser Cache TTL: 1 day

Performance Optimization

Image Processing

const sharpOptions = {
  limitInputPixels: 268402689,  // ~16k x 16k max
  sequentialRead: true,
  density: 72
};

Concurrency Control

The media proxy uses a coalescer to prevent duplicate processing:
import { InMemoryCoalescer } from '@fluxer/media_proxy';

const coalescer = new InMemoryCoalescer();

// Only process once for concurrent requests
const result = await coalescer.coalesce(key, async () => {
  return await processImage(buffer, options);
});

Monitoring

Metrics

The media proxy tracks:
  • Request count by endpoint and status
  • Processing time for transformations
  • Cache hit ratio
  • Error rate by type
  • Bandwidth usage

Health Check

GET /health
Response:
{
  "status": "healthy",
  "uptime": 86400,
  "version": "1.0.0"
}

Best Practices

Use WebP

Request WebP format for best compression and quality. It’s supported by all modern browsers.

Specify Dimensions

Always specify width/height to allow the proxy to optimize processing and caching.

Leverage Caching

Use consistent URLs for the same transformations to maximize cache hits.

Quality vs Size

Use medium quality for most images. Use high only for important images where quality matters.

Client Integration

function getOptimizedImageUrl(
  hash: string,
  options: {
    width?: number;
    height?: number;
    quality?: 'low' | 'medium' | 'high';
    format?: 'webp' | 'png' | 'jpeg';
  } = {}
): string {
  const params = new URLSearchParams();
  
  if (options.width) params.set('width', String(options.width));
  if (options.height) params.set('height', String(options.height));
  if (options.quality) params.set('quality', options.quality);
  if (options.format) params.set('format', options.format);
  
  return `https://cdn.fluxer.app/proxy/image/${hash}?${params}`;
}

// Usage
const avatarUrl = getOptimizedImageUrl('abc123', {
  width: 128,
  height: 128,
  format: 'webp',
  quality: 'medium'
});

Build docs developers (and LLMs) love