How to Implement Path Rewriting in AWS ALB Without Nginx: A Technical Deep Dive


2 views

When working with Amazon Application Load Balancer (ALB), many developers encounter the same challenge: the ALB's native path-based routing doesn't support path rewriting. The standard configuration allows routing like:

my-alb-dns.com:80/container-a/* → container-a:8080/*
my-alb-dns.com:80/container-b/* → container-b:8080/*

But what if we need to remove the /container-a/ or /container-b/ prefix before forwarding to the target? This is where developers typically resort to Nginx as a middleware solution.

Before implementing custom solutions, let's examine AWS-native options:

  • ALB Rules: While ALB supports path-based routing (since April 2017), it lacks path rewriting capabilities
  • Lambda@Edge: Works with CloudFront but introduces complexity and additional costs
  • API Gateway: Overkill for simple path rewriting between containers

One elegant solution is to use AWS Lambda as an ALB target for path manipulation:

exports.handler = async (event) => {
    const request = event.Records[0].cf.request;
    const uri = request.uri;
    
    // Example: Rewrite /container-a/path/to/resource to /path/to/resource
    if (uri.startsWith('/container-a/')) {
        request.uri = uri.replace('/container-a/', '/');
    }
    
    return request;
};

To configure this:

  1. Create a Lambda function with ALB trigger
  2. Set the Lambda as target for your ALB path pattern
  3. Ensure Lambda has proper IAM permissions for ELB

While the Lambda solution works, consider these factors:

Solution Latency Cost Maintenance
Nginx Low Medium (EC2 costs) Medium
Lambda Higher (cold starts) Low (pay per use) Low

While AWS hasn't officially announced path rewriting for ALB, we can anticipate this feature based on:

  • Growing developer demand
  • Similar features in competitors' products
  • ALB's continuous evolution (recently added weighted routing)

For most production scenarios currently, I recommend:

ALB → Nginx (path rewriting) → ECS Containers

Sample Nginx configuration:

location /container-a/ {
    proxy_pass http://container-a:8080/;
    rewrite ^/container-a/(.*) /$1 break;
}

location /container-b/ {
    proxy_pass http://container-b:8080/;
    rewrite ^/container-b/(.*) /$1 break;
}

Many developers face this exact scenario when working with Amazon Application Load Balancer:

http://my-alb-dns.com/container-a/api/users → container-a:8080/api/users
http://my-alb-dns.com/container-b/api/orders → container-b:8080/api/orders

While this can be achieved with an NGINX reverse proxy (which adds infrastructure complexity), AWS ALB doesn't natively support path rewriting as of 2023.

1. Using ALB Path Patterns with Target Groups:

# Example ALB Listener Rule (AWS CLI format)
aws elbv2 create-rule \
    --listener-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/my-load-balancer/1234567890123456/1234567890123456 \
    --priority 10 \
    --conditions Field=path-pattern,Values='/container-a/*' \
    --actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/container-a/1234567890123456

Limitation: This forwards the complete path including '/container-a' to your backend service.

Option 1: API Gateway + ALB Integration

# CloudFormation snippet for API Gateway mapping
Resources:
  ApiMapping:
    Type: AWS::ApiGatewayV2::ApiMapping
    Properties:
      ApiId: !Ref MyHttpApi
      DomainName: api.example.com
      Stage: !Ref ProdStage
      ApiMappingKey: "container-a"

Option 2: Lambda@Edge for Path Rewriting

// Lambda@Edge viewer request handler
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    
    // Rewrite /container-a/api/ to /api/
    if (request.uri.startsWith('/container-a/')) {
        request.uri = request.uri.replace('/container-a', '');
    }
    
    callback(null, request);
};

For complex rewriting needs, an EC2 instance or ECS container running NGINX might still be optimal:

# NGINX configuration example
server {
    listen 80;
    
    location /container-a/ {
        proxy_pass http://container-a:8080/;
        rewrite ^/container-a/(.*) /$1 break;
    }
    
    location /container-b/ {
        proxy_pass http://container-b:8080/;
        rewrite ^/container-b/(.*) /$1 break;
    }
}

Based on AWS's history of gradually adding ALB features like:

  • Host-based routing (2017)
  • Weighted target groups (2019)
  • HTTP/2 and gRPC support (2020)

Native path rewriting functionality may eventually arrive. Watch the ALB release notes for updates.