A fast caching and optimization media proxy for Akkoma/Pleroma, built in Rust.
- Caching Reverse Proxy: Caches media and proxy requests to reduce load on upstream servers
- Header Preservation: Preserves all upstream headers by default, including redirects (302) with Location headers
- Image Format Conversion: Automatically converts images to modern formats (AVIF, WebP) based on client
Acceptheaders - Path Filtering: Only handles
/mediaand/proxyendpoints for security - Performance: Built with Tokio async runtime for high concurrency
- Flexible Configuration: TOML-based configuration with environment variable and CLI overrides
- Out-of-the-box: Works with just an upstream URL, no complex setup needed
The simplest way to start:
UPSTREAM_URL=https://your-akkoma-instance.com ./akkoproxyCreate a config.toml file:
[upstream]
url = "https://your-akkoma-instance.com"Then run:
./akkoproxySee config.example.toml for all available options.
Download the latest release from the releases page.
docker run -p 3000:3000 \
-e UPSTREAM_URL=https://your-akkoma-instance.com \
ghcr.io/blockg-ws/akkoma-media-proxy:latestWith configuration file:
docker run -p 3000:3000 \
-v $(pwd)/config.toml:/app/config.toml \
ghcr.io/blockg-ws/akkoma-media-proxy:latestRequirements:
- Rust 1.84 or later (for edition2024 support required by dependencies)
cargo build --release
./target/release/akkoproxyConfiguration is loaded with the following priority (highest to lowest):
- Environment Variables (highest priority)
- Command-line Options
- Configuration File (lowest priority)
This means environment variables will override command-line options, which will override settings in the config file.
[upstream]
url = "https://your-akkoma-instance.com" # Required: Your Akkoma/Pleroma instance
timeout = 30 # Request timeout in seconds[server]
bind = "0.0.0.0:3000" # Bind address
via_header = "akkoma-media-proxy/0.1.0" # Via header value
preserve_upstream_headers = true # Preserve all headers from upstream (default: true)
behind_cloudflare_free = false # Enable Cloudflare Free plan compatibility (default: false)When using Cloudflare's Free plan (which doesn't support Vary on cached content based on headers), you can enable behind_cloudflare_free = true to make the proxy work better with Cloudflare's Transform Rules.
How it works:
- Set
behind_cloudflare_free = truein your config - The proxy will look for a
formatquery parameter in the URL- If
format=avifis present, the image will be converted to AVIF - If
format=webpis present, the image will be converted to WebP
- If
- The
formatparameter is stripped from the upstream request - A
Vary: Acceptheader is added to responses - The
Access-Control-Allow-Originheader from upstream is preserved (not replaced)
Cloudflare Transform Rule Setup:
Create a Transform Rule in Cloudflare to add the format query parameter based on the Accept header:
- Go to your domain in Cloudflare Dashboard → Rules → Transform Rules → Modify Request URL
- Add a new rule:
Rule 1: Add format=avif for AVIF support
- If:
(http.request.uri.path matches "^/(media|proxy)" and http.request.headers["accept"][*] contains "image/avif") - Then: Query → Add →
format=avif
Rule 2: Add format=webp for WebP support (with lower priority)
- If:
(http.request.uri.path matches "^/(media|proxy)" and http.request.headers["accept"][*] contains "image/webp" and not http.request.headers["accept"][*] contains "image/avif") - Then: Query → Add →
format=webp
This setup allows Cloudflare to cache different formats separately based on the query parameter, working around the Free plan's limitation on Vary header support.
[cache]
max_capacity = 10000 # Maximum number of cached items
ttl = 3600 # Cache TTL in seconds (1 hour)
max_item_size = 10485760 # Maximum cacheable item size (10MB)[image]
enable_avif = true # Enable AVIF conversion
enable_webp = true # Enable WebP conversion
quality = 85 # JPEG quality (1-100)
max_dimension = 4096 # Maximum image dimension- Request Filtering: Only
/mediaand/proxypaths are allowed - Cache Check: Looks for cached response with the requested format
- Upstream Fetch: If not cached, fetches from upstream server
- Header Preservation: All upstream headers (including Location for redirects) are preserved by default
- Image Conversion: For images, converts to the best format based on
Acceptheader:- Prefers AVIF if
image/avifis accepted - Falls back to WebP if
image/webpis accepted - Otherwise returns original or JPEG
- Prefers AVIF if
- Caching: Stores the converted response for future requests
- Response: Returns the optimized content with appropriate headers
The proxy respects the Accept header for image format selection:
Accept: image/avif,image/webp,image/*;q=0.8
Priority order:
- AVIF (if enabled and accepted)
- WebP (if enabled and accepted)
- JPEG (fallback)
GET /media/*- Proxied media requests with caching and conversionGET /proxy/*- Proxied proxy requests with caching and conversionGET /health- Health check endpointGET /metrics- Cache metrics (Prometheus-compatible)
- Path Restriction: Only
/mediaand/proxypaths are allowed - No Directory Traversal: Path validation prevents directory traversal attacks
- Timeout Protection: Upstream requests have configurable timeouts
- Size Limits: Configurable maximum cache item size
- TLS: Uses rustls for secure HTTPS connections to upstream
- Async I/O: Built on Tokio for efficient concurrent request handling
- Smart Caching: LRU cache with TTL and size-based eviction
- Connection Pooling: Reuses HTTP connections to upstream
- Efficient Image Processing: Uses optimized image libraries
cargo buildcargo testUPSTREAM_URL=https://example.com cargo runEnvironment variables have the highest priority and will override both command-line options and config file settings:
UPSTREAM_URL: Upstream server URL (overrides config file and CLI option)BIND_ADDRESS: Server bind address (e.g.,0.0.0.0:3000)PRESERVE_HEADERS: Preserve upstream headers (trueorfalse)RUST_LOG: Logging level (e.g.,debug,info,warn,error)
Command-line options have medium priority and will override config file settings:
akkoproxy [OPTIONS]
Options:
-c, --config <FILE> Path to configuration file
-u, --upstream <URL> Upstream server URL
-b, --bind <ADDR> Address to bind the server to
--enable-avif Enable AVIF conversion
--disable-avif Disable AVIF conversion
--enable-webp Enable WebP conversion
--disable-webp Disable WebP conversion
--preserve-headers Preserve all headers from upstream
-h, --help Print help
-V, --version Print version# Config file: config.toml
[upstream]
url = "https://config-url.com"
# Command line
./akkoproxy --upstream https://cli-url.com
# Environment variable
UPSTREAM_URL=https://env-url.com ./akkoproxy --upstream https://cli-url.com
# Result: https://env-url.com (environment has highest priority)Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contributions are welcome! Please feel free to submit a Pull Request.