A lightweight, fast, self-hosted shortlink redirection service written in Rust using Axum and Sqlx with SQLite.
- Fast HTTP 302 Redirection: Instant lookups backed by SQLite.
- Secure Link Management: Create, update, and delete links, all protected by Bearer token authentication with constant-time equality checks to mitigate timing attacks.
- URL Scheme Validation: Only accepts
http://andhttps://URLs to prevent protocol abuse. - Default Fallback: Visiting
/redirects to a configurable landing page or repository URL. - Health Check Endpoint: Probes the SQLite database schema for zero-downtime health monitors and container orchestrators.
- Automatic Migrations: Embedded SQLx migrations executed at application startup.
- Structured Logging: Powered by
tracingwith configurable log levels.
The fastest way to run redirectr is via Docker. The image already binds SERVER_IP=0.0.0.0 and points DATABASE_URL at /data; mount a volume there to ensure links persist across container restarts.
docker run -d \
--name redirectr \
-p 3333:3333 \
-v redirectr_data:/data \
-e ADMIN_TOKEN="your-super-secret-token" \
-e DEFAULT_URL="https://example.com" \
docker.io/andybzn/redirectr:latestCreate a docker-compose.yml file:
services:
redirectr:
image: docker.io/andybzn/redirectr:latest
container_name: redirectr
restart: unless-stopped
ports:
- "3333:3333"
environment:
ADMIN_TOKEN: "your-super-secret-token"
DEFAULT_URL: "https://example.com"
RUST_LOG: "redirectr=info,sqlx=warn"
volumes:
- redirectr_data:/data
volumes:
redirectr_data:Run with:
docker compose up -dredirectr is configured entirely through environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
ADMIN_TOKEN |
Yes | — | Bearer token required to create, update, or delete links via POST /, PATCH /, and DELETE /{code}. |
SERVER_IP |
No | 127.0.0.1 |
IP address to bind the HTTP server to (0.0.0.0 for Docker). |
SERVER_PORT |
No | 3333 |
TCP port the HTTP server listens on. |
DATABASE_URL |
No | sqlite://redirectr.sqlite |
SQLite connection URL (file created automatically if missing). |
MAX_CONN |
No | 9 |
Maximum SQLite connection pool size. |
DEFAULT_URL |
No | https://github.com/andybzn/redirectr |
Fallback destination for GET /. |
RUST_LOG |
No | redirectr=debug,sqlx=warn |
tracing subscriber log filter level. |
- Route:
GET /{code} - Response:
302 FoundwithLocation: <target_url>on success404 Not Foundif the code does not exist
curl -i http://localhost:3333/my-link- Route:
POST /?code=<code>&url=<target_url> - Headers:
Authorization: Bearer <ADMIN_TOKEN> - Response:
201 Createdon success400 Bad Requestif the target URL is invalid or uses an unsupported scheme401 Unauthorizedif the bearer token is missing or invalid409 Conflictif thecodeis already in use
curl -i -X POST "http://localhost:3333/?code=gh&url=https%3A%2F%2Fgithub.com%2Fandybzn%2Fredirectr" \
-H "Authorization: Bearer your-super-secret-token"- Route:
PATCH /?code=<code>&url=<target_url> - Headers:
Authorization: Bearer <ADMIN_TOKEN> - Response:
204 No Contenton success400 Bad Requestif the target URL is invalid or uses an unsupported scheme401 Unauthorizedif the bearer token is missing or invalid404 Not Foundif thecodedoes not exist
curl -i -X PATCH "http://localhost:3333/?code=gh&url=https%3A%2F%2Fgithub.com%2Fandybzn%2Fredirectr2" \
-H "Authorization: Bearer your-super-secret-token"- Route:
DELETE /{code} - Headers:
Authorization: Bearer <ADMIN_TOKEN> - Response:
204 No Contenton success, whether or not the code existed401 Unauthorizedif the bearer token is missing or invalid
curl -i -X DELETE "http://localhost:3333/gh" \
-H "Authorization: Bearer your-super-secret-token"- Route:
GET / - Response:
302 Foundredirecting to the configuredDEFAULT_URL.
curl -i http://localhost:3333/- Route:
GET /health - Response:
200 OKwhen the database andlinkstable are accessible503 Service Unavailableon database failure
curl -i http://localhost:3333/health# Clone the repository
git clone https://github.com/andybzn/redirectr.git
cd redirectr
# Run locally with cargo
ADMIN_TOKEN="dev-secret-token" cargo runIf you use devenv, all dependencies and tasks are pre-configured:
# Enter development shell
devenv shell
# Run all test and lint tasks
devenv tasks run app:tests# Run unit and integration tests
cargo test
# Run Clippy with strict checks
cargo clippy --all-targets -- -D warnings
# Check code formatting
cargo fmt --check
# Re-generate SQLx query metadata cache (if SQL queries are modified)
cargo sqlx prepareThis project is licensed under the terms of the MIT License