Apache
Integrate redirect lookups with Apache HTTP Server using mod_rewrite and the Edge Query API
Apache Integration Guide
This guide shows you how to integrate redirect lookups into Apache HTTP Server using mod_rewrite and the Edge Query API. You'll learn both real-time API lookup and periodic sync patterns.
Estimated time: 30 minutes
Overview
By the end of this guide, you'll have Apache automatically handling redirects by querying the Edge Query API or using locally synced configuration files. Incoming requests for old paths will be redirected to new destinations without manual configuration file updates.
Prerequisites
- Apache 2.4+ with
mod_rewriteenabled curlcommand-line tool (for testing and sync script)- An API key from your project's Settings > API Keys page
- Your project ID (found in the project dashboard URL)
What You'll Build
flowchart LR
A[Client Request] --> B[Apache]
B --> C{RewriteEngine}
C --> D[Lookup Script/Map]
D --> E{Match Found?}
E -->|Yes| F[301 Redirect]
E -->|No| G[Backend/Pass-through]
F --> APattern 1: Real-Time API Lookup
This pattern uses Apache's RewriteMap with a program-type external lookup script. Each request triggers an API call to fetch redirect destinations in real-time.
Step 1: Create the Lookup Script
Create /usr/local/bin/redirects-lookup.pl with the following Perl script:
#!/usr/bin/perl
# CRITICAL: Disable output buffering for RewriteMap program
$| = 1;
use strict;
use warnings;
# Configuration - read from environment or hardcode
my $api_key = $ENV{'REDIRECTS_API_KEY'} || 'YOUR_API_KEY';
my $project_id = $ENV{'REDIRECTS_PROJECT_ID'} || 'YOUR_PROJECT_ID';
my $api_url = "https://api.3xx.app/v1/lookup";
# Main loop: read paths from stdin, write destinations to stdout
while (my $path = <STDIN>) {
chomp($path);
# URL encode the path
$path =~ s/([^A-Za-z0-9\-._~\/])/sprintf("%%%02X", ord($1))/seg;
# Call API with timeout
my $cmd = qq{curl -s -m 2 -H "X-API-Key: $api_key" -H "X-Project-ID: $project_id" "$api_url?path=$path"};
my $response = `$cmd`;
# Parse JSON response
if ($response =~ /"destination":"([^"]+)"/) {
print "$1\n";
} else {
# No match - return NULL to let request pass through
print "NULL\n";
}
}Make the script executable:
sudo chmod +x /usr/local/bin/redirects-lookup.plWhy $| = 1 is critical: Apache's RewriteMap expects immediate output for each input line. Without disabling buffering, Perl buffers output, causing Apache to hang waiting for responses.
Step 2: Configure Apache RewriteMap
Edit your Apache configuration file (typically /etc/apache2/sites-available/000-default.conf or /etc/httpd/conf/httpd.conf) and add the RewriteMap directive inside the VirtualHost block:
<VirtualHost *:80>
ServerName example.com
# Define the external program lookup map
RewriteMap redirects "prg:/usr/local/bin/redirects-lookup.pl"
# Set environment variables for the script
SetEnv REDIRECTS_API_KEY "YOUR_API_KEY"
SetEnv REDIRECTS_PROJECT_ID "YOUR_PROJECT_ID"
<Location />
RewriteEngine On
# Query the map for redirect destination
RewriteCond ${redirects:%{REQUEST_URI}} !^NULL$
RewriteRule ^(.*)$ ${redirects:%{REQUEST_URI}} [R=301,L]
</Location>
# Your existing backend configuration
ProxyPass /api http://backend:8080/api
ProxyPassReverse /api http://backend:8080/api
</VirtualHost>Verification: Test the configuration syntax:
sudo apachectl configtestYou should see: Syntax OK
Step 3: Restart Apache
sudo systemctl restart apache2 # Debian/Ubuntu
# OR
sudo systemctl restart httpd # RHEL/CentOSVerification: Test a redirect:
curl -I http://localhost/old-pathYou should see a 301 Moved Permanently response with a Location header pointing to the new destination.
If you see 200 OK or the backend response, the path has no redirect rule configured (expected behavior for non-redirected paths).
Pattern 2: Periodic Sync + Local Config
This pattern fetches redirect rules from the Export API periodically and generates Apache Redirect directives in a local file. Apache loads this file directly for zero-latency lookups.
Step 1: Create Sync Script
Create /usr/local/bin/sync-redirects.sh:
#!/bin/bash
API_KEY="YOUR_API_KEY"
PROJECT_ID="YOUR_PROJECT_ID"
API_URL="https://api.3xx.app/v1/export"
OUTPUT_FILE="/etc/apache2/redirects.conf"
TEMP_FILE="${OUTPUT_FILE}.tmp"
# Fetch redirects and generate Apache directives
curl -s -H "X-API-Key: ${API_KEY}" -H "X-Project-ID: ${PROJECT_ID}" \
"${API_URL}?format=csv" | \
awk -F',' 'NR>1 {print "Redirect 301 " $1 " " $2}' > "${TEMP_FILE}"
# Atomic replacement (prevents partial file reads during reload)
mv "${TEMP_FILE}" "${OUTPUT_FILE}"
# Graceful reload (picks up new config without dropping connections)
apachectl graceful
echo "$(date): Synced $(wc -l < ${OUTPUT_FILE}) redirects"Make it executable:
sudo chmod +x /usr/local/bin/sync-redirects.shWhy temp file + mv: The mv command is atomic on most filesystems. This prevents Apache from reading a partially-written file during reload, which could cause redirect rules to be incomplete or malformed.
Step 2: Configure Apache to Include Redirects
Edit your Apache configuration:
<VirtualHost *:80>
ServerName example.com
# Include dynamically synced redirects
Include /etc/apache2/redirects.conf
# Your existing backend configuration
ProxyPass /api http://backend:8080/api
ProxyPassReverse /api http://backend:8080/api
</VirtualHost>Verification: Run the sync script manually:
sudo /usr/local/bin/sync-redirects.shCheck that redirects were synced:
cat /etc/apache2/redirects.confYou should see Apache Redirect directives. Test a redirect:
curl -I http://localhost/old-pathStep 3: Schedule with Cron
Add to crontab (run sudo crontab -e):
# Sync redirects every 5 minutes
*/5 * * * * /usr/local/bin/sync-redirects.sh >> /var/log/redirects-sync.log 2>&1Alternatives:
- systemd timer: More robust than cron with logging and dependency management
- CI/CD webhook: Trigger sync immediately after redirect rule changes in dashboard
Fallback Behavior
Both patterns are designed to fail gracefully:
Real-Time Lookup Pattern
If the API is unreachable or times out:
- The lookup script returns
NULL - Apache treats it as "no redirect found"
- Request passes through to backend (normal behavior)
Timeout handling: The -m 2 flag in curl sets a 2-second timeout. Adjust based on your latency tolerance.
Periodic Sync Pattern
If the sync script fails:
- Previous redirects.conf remains in place
- Redirects continue working with stale data
- Check
/var/log/redirects-sync.logfor errors
Advanced: Add caching to the real-time pattern using Apache's mod_cache to reduce API calls for frequently-accessed paths.
Testing
Test comprehensive redirect scenarios:
Test 1: Exact Match
Create a redirect rule in the dashboard: /old-page → https://example.com/new-page
curl -I http://localhost/old-pageExpected: 301 Moved Permanently with Location: https://example.com/new-page
Test 2: Prefix Match
Create a prefix redirect: /blog/* → https://blog.example.com/*
curl -I http://localhost/blog/2024/post-titleExpected: 301 with Location: https://blog.example.com/blog/2024/post-title
Test 3: No Match (Pass-Through)
curl -I http://localhost/api/usersExpected: Backend response (200, 404, etc.) - not a redirect
Test 4: API Failure (Real-Time Pattern Only)
Temporarily break the API key to simulate failure:
# Edit /usr/local/bin/redirects-lookup.pl and change API_KEY to invalid value
sudo systemctl restart apache2
curl -I http://localhost/old-pageExpected: Backend response (graceful degradation, no error)
Restore the correct API key and restart Apache.
Troubleshooting
RewriteMap Program Hangs
Symptom: Apache hangs on startup or requests timeout
Cause: Lookup script output is buffered (Perl, Python)
Fix: Add unbuffered output at the top of your script:
- Perl:
$| = 1; - Python:
import sys; sys.stdout.reconfigure(line_buffering=True) - Bash: Use
stdbuf -oLwrapper
Permission Errors on Lookup Script
Symptom: (2)No such file or directory: failed to start process
Cause: Script not executable or wrong path
Fix:
sudo chmod +x /usr/local/bin/redirects-lookup.pl
sudo chown root:root /usr/local/bin/redirects-lookup.plmod_rewrite Not Enabled
Symptom: Invalid command 'RewriteEngine'
Fix:
sudo a2enmod rewrite # Debian/Ubuntu
sudo systemctl restart apache2Environment Variables Not Accessible
Symptom: Script sees empty $ENV{'REDIRECTS_API_KEY'}
Cause: Apache restricts environment variable passing to child processes
Fix: Either hardcode values in the script or use PassEnv directive:
PassEnv REDIRECTS_API_KEY
PassEnv REDIRECTS_PROJECT_IDCommon API Issues
For authentication errors, rate limiting, quota exhaustion, and other API-level problems, see the Troubleshooting Guide.
Next Steps
- Review API Reference for advanced lookup options
- Explore Export Formats for optimized sync patterns
- Set up monitoring for API quota usage in project dashboard
- Consider migrating to Periodic Sync pattern for high-traffic applications