Redirections
Integration Guides

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_rewrite enabled
  • curl command-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 --> A

Pattern 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.pl

Why $| = 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 configtest

You should see: Syntax OK

Step 3: Restart Apache

sudo systemctl restart apache2  # Debian/Ubuntu
# OR
sudo systemctl restart httpd    # RHEL/CentOS

Verification: Test a redirect:

curl -I http://localhost/old-path

You 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.sh

Why 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.sh

Check that redirects were synced:

cat /etc/apache2/redirects.conf

You should see Apache Redirect directives. Test a redirect:

curl -I http://localhost/old-path

Step 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>&1

Alternatives:

  • 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.log for 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-pagehttps://example.com/new-page

curl -I http://localhost/old-page

Expected: 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-title

Expected: 301 with Location: https://blog.example.com/blog/2024/post-title

Test 3: No Match (Pass-Through)

curl -I http://localhost/api/users

Expected: 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-page

Expected: 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 -oL wrapper

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.pl

mod_rewrite Not Enabled

Symptom: Invalid command 'RewriteEngine'

Fix:

sudo a2enmod rewrite        # Debian/Ubuntu
sudo systemctl restart apache2

Environment 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_ID

Common 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