URL Routing and Rewrites¶
Navigator provides flexible URL routing with support for rewrites, redirects, and Fly.io's intelligent replay system for regional deployments.
Basic Routing¶
Navigator routes requests in this order:
- Static files - Direct filesystem serving
- Rewrite rules - URL transformations
- Authentication - Access control
- Applications - Rails app routing by path prefix
Rewrite Rules¶
Transform URLs before they reach your Rails application:
routes:
rewrites:
# Redirect old URLs
- pattern: "^/old-blog/(.*)"
replacement: "/blog/$1"
redirect: true
status: 301
# Internal rewrite (no redirect)
- pattern: "^/api/v1/(.*)"
replacement: "/api/latest/$1"
redirect: false
# Simple redirects
- pattern: "^/home$"
replacement: "/"
redirect: true
status: 302
Rewrite Configuration¶
Field | Type | Default | Description |
---|---|---|---|
pattern |
string | - | Regular expression pattern to match |
replacement |
string | - | Replacement string (supports $1 , $2 capture groups) |
redirect |
boolean | false |
Send HTTP redirect vs internal rewrite |
status |
integer | 302 |
HTTP status code for redirects |
Pattern Examples¶
routes:
rewrites:
# Capture groups with ()
- pattern: "^/user/([0-9]+)$"
replacement: "/users/$1"
# Multiple capture groups
- pattern: "^/([0-9]{4})/([a-z]+)/(.*)$"
replacement: "/$1/$2/events/$3"
# Case-insensitive matching
- pattern: "(?i)^/API/(.*)"
replacement: "/api/$1"
# Remove file extensions
- pattern: "^/(.*)\\.html$"
replacement: "/$1"
Fly-Replay Routing¶
Route requests to specific Fly.io regions, applications, or machines for optimal performance:
routes:
fly_replay:
# Region-based routing
- path: "^/sydney/"
region: syd
status: 307
methods: [GET, POST]
# App-based routing
- path: "^/.*\\.pdf$"
app: pdf-generator
status: 307
# Machine-specific routing
- path: "^/priority/.*"
machine: "48e403dc711e18"
app: priority-processor
status: 307
Fly-Replay Configuration¶
Field | Type | Required | Description |
---|---|---|---|
path |
string | ✓ | URL regex pattern |
region |
string | Target Fly.io region code | |
app |
string | Target Fly.io application name | |
machine |
string | Specific machine ID (requires app ) |
|
status |
integer | 307 |
HTTP status code |
methods |
array | HTTP methods to match |
Fly.io Region Codes¶
Region | Code | Location |
---|---|---|
North America | ||
Ashburn | iad |
Virginia, US |
Chicago | ord |
Illinois, US |
Los Angeles | lax |
California, US |
Seattle | sea |
Washington, US |
Toronto | yyz |
Ontario, Canada |
Europe | ||
Amsterdam | ams |
Netherlands |
Frankfurt | fra |
Germany |
London | lhr |
United Kingdom |
Asia Pacific | ||
Hong Kong | hkg |
China |
Singapore | sin |
Singapore |
Sydney | syd |
Australia |
Tokyo | nrt |
Japan |
Smart Fallback¶
Navigator automatically falls back to reverse proxy for requests that can't use Fly-Replay:
- Large requests (>1MB) - proxied directly to avoid replay limits
- Non-GET/HEAD methods - for safety with state-changing operations
- WebSocket upgrades - require persistent connections
Routing Patterns¶
1. Multi-Region Deployment¶
routes:
fly_replay:
# Route by geographic prefix
- path: "^/asia/"
region: sin
methods: [GET]
- path: "^/europe/"
region: fra
methods: [GET]
- path: "^/americas/"
region: ord
methods: [GET]
# Heavy processing to dedicated region
- path: "^/reports/"
region: fra # EU region with more CPU
methods: [POST]
2. Service-Specific Routing¶
routes:
fly_replay:
# PDF generation service
- path: "^/.*\\.pdf$"
app: pdf-service
# Image processing
- path: "^/images/resize/"
app: image-processor
# Search service
- path: "^/search/"
app: search-engine
3. Load Balancing by Machine¶
routes:
fly_replay:
# High-priority requests to specific machine
- path: "^/priority/"
machine: "e24a0123456"
app: main-app
# Regular traffic uses default routing
# (no fly_replay rule = normal load balancing)
4. Development vs Production Routing¶
URL Rewriting Examples¶
Legacy URL Support¶
routes:
rewrites:
# Old WordPress URLs
- pattern: "^/\\?p=([0-9]+)$"
replacement: "/posts/$1"
redirect: true
status: 301
# Old category URLs
- pattern: "^/category/(.+)$"
replacement: "/categories/$1"
redirect: true
status: 301
# Remove trailing slashes
- pattern: "^(.+)/$"
replacement: "$1"
redirect: true
status: 301
API Versioning¶
routes:
rewrites:
# Default to latest API version
- pattern: "^/api/([^/]+)$"
replacement: "/api/v3/$1"
redirect: false
# Redirect old versions
- pattern: "^/api/v[12]/(.*)"
replacement: "/api/v3/$1"
redirect: true
status: 301
Multi-Language Sites¶
routes:
rewrites:
# Default to English
- pattern: "^/([^/]+)$"
replacement: "/en/$1"
redirect: false
# Language-specific routing
- pattern: "^/(en|es|fr)/(.*)$"
replacement: "/$2?lang=$1"
redirect: false
Application Path Routing¶
Define which Rails applications handle which URL paths:
applications:
tenants:
# API application
- name: api
path: /api/
working_dir: /var/www/api
# Admin application
- name: admin
path: /admin/
working_dir: /var/www/admin
# Main application (catch-all)
- name: main
path: /
working_dir: /var/www/main
Path Matching Rules¶
- Exact prefix match:
/api/
matches/api/users
but not/api-v2/users
- Longest match wins:
/api/v2/
takes precedence over/api/
- Order matters: First matching path is used
Advanced Path Patterns¶
applications:
tenants:
# Pattern matching with wildcards
- name: tenant-sites
path: /sites/*/
match_pattern: "/sites/*/admin"
# Multiple paths for same app
- name: legacy
path: /old/
- name: legacy-alt
path: /legacy/
# Both route to same Rails app
HTTP Method Filtering¶
Control which HTTP methods trigger routing rules:
routes:
fly_replay:
# Only GET requests to region
- path: "^/catalog/"
region: syd
methods: [GET, HEAD]
# POST requests to processing service
- path: "^/process/"
app: processor
methods: [POST, PUT]
applications:
tenants:
# Exclude dangerous methods from API
- name: readonly-api
path: /api/read/
exclude_methods: [POST, PUT, DELETE, PATCH]
Error Handling¶
Custom Error Pages¶
routes:
rewrites:
# Custom 404 page
- pattern: "^/404$"
replacement: "/errors/not_found"
redirect: false
# Custom 500 page
- pattern: "^/500$"
replacement: "/errors/server_error"
redirect: false
Maintenance Mode¶
routes:
rewrites:
# Redirect everything to maintenance page
- pattern: "^/(?!maintenance).*$"
replacement: "/maintenance"
redirect: true
status: 503
# Except for assets
static:
directories:
- path: /assets/
root: public/assets/
Testing Routing Rules¶
Manual Testing¶
# Test redirects
curl -I http://localhost:3000/old-blog/post1
# Should show 301 redirect
# Test rewrites
curl -v http://localhost:3000/api/users
# Check final URL in logs
# Test fly-replay
curl -I http://localhost:3000/sydney/products
# Should show 307 with JSON body
Automated Testing¶
#!/bin/bash
# Test routing rules
# Test redirect
response=$(curl -s -o /dev/null -w "%{http_code}" -L http://localhost:3000/old-url)
if [ "$response" = "200" ]; then
echo "✓ Redirect working"
else
echo "✗ Redirect failed: $response"
fi
# Test rewrite
response=$(curl -s http://localhost:3000/api/test)
if echo "$response" | grep -q "expected content"; then
echo "✓ Rewrite working"
else
echo "✗ Rewrite failed"
fi
Performance Considerations¶
1. Pattern Complexity¶
# Fast - simple prefix
- pattern: "^/api/"
replacement: "/v3/api/"
# Slower - complex regex
- pattern: "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/(.+)$"
replacement: "/date/$1-$2-$3/$4"
2. Rule Order¶
routes:
rewrites:
# Put specific rules first
- pattern: "^/api/v3/special"
replacement: "/special-handler"
# General rules last
- pattern: "^/api/v3/(.*)"
replacement: "/api/$1"
3. Fly-Replay Optimization¶
routes:
fly_replay:
# Use specific patterns to avoid unnecessary checks
- path: "^/heavy-compute/" # Specific
region: fra
# Avoid overly broad patterns
# - path: ".*" # Matches everything!
Troubleshooting¶
Rules Not Matching¶
-
Test patterns:
-
Check order: More specific rules should come first
-
Escape special characters:
Redirects Not Working¶
-
Check redirect flag:
-
Verify status code: Default is 302 (temporary)
Fly-Replay Issues¶
- Check app/region exists: Invalid targets will fail
- Large requests: >1MB automatically use reverse proxy
- Method restrictions: Some methods may not work with replay