Are you using AWS Elastic Load Balancers (ELB) or Elastic Beanstalk (EB) and want to decide just one root url with domain and redirect other URL’s to that? We’ll have a look at that to do on AWS. It’s a vital thing to avoid confusion about accessing your web app.

SSL/TLS on AWS EC2 and ELB

I assume that you’ve already set up your EC2 instances with an application load balancer and enabled SSL/TLS using AWS Certificate Manager (ACM). Notice that you’re terminating SSL on load balancer. Thus, EC2 instances get decrypted traffic and we’ll route and manage traffic on load balancer, unaware of actual application servers. For elastic beanstalk scenarios, the same is valid since you’ve configured these load balancer options while creating the elastic beanstalk app.

Deciding the root domain and url is quite important because otherwise it causes problems on session validity, SSL certificate problems, duplicate indexes on Google searches and more. In an ideal world, any web app with non-www/www and http/https versions should point same. On this tutorial, we’ll point http://site.com, http://www.site.com and https://site.com URL’s to https://www.site.com

On AWS Console

Go to Load Balancers section at the AWS EC2 console. Find and select the load balancer you’ll configure. (If you created it with Elastic Beanstalk, it should have a prefix as awseb-) On Listeners pane, you should see HTTP:80 and HTTPS:443 listeners. If you only have an HTTPS listener, requests to your HTTP urls will fail, so you need to create HTTP:80 one just for redirecting purposes. After, find the View/edit rules link.

Click the edit button on the top bar in the rule editor and press the edit icon on the left side of the default rule. We’re editing the default rule since we want to redirect traffic to HTTPS url instead of balancer.

On the THEN section, select Redirect to... and select Custom host, path, query and fill the form like mine below.

As you can notice, we’re redirecting both http://site.com, http://www.site.com requests to https://www.site.com. So we have just one scenario to cover; https://site.com to https://www.site.com

To achieve this, find the View/edit rules link for HTTPS:443 listener. On this listener, we want to move traffic to target group except traffic from https://site.com. Default rule will remain same, we’ll add a new rule by using (+) button on the top bar. Just like the HTTP:80’s rule, we’ll add a rule for root domain.

As final, you should see overview of the configuration we’ve just made at the load balancers page.

NOTE: You need to point ELB public DNS alias for your root domain and www domain using Route53 to use both domains. You can get public DNS from Description > Basic Configuration > DNS Name section.

On AWS CLI

To accelerate the manual operation on AWS Console, you can add and edit them via AWS CLI. This command adds a new rule to given listener-arn.

aws elbv2 create-rule
--listener-arn arn:aws:elasticloadbalancing:us-west-2:123456789012:listener/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2
--priority 1
--conditions Field=host-header,Values='site.com'
--actions Type=redirect,Order=1,RedirectConfig={Protocol=HTTPS,Port=#{port},Host=www.site.com,Path=/#{path},Query=#{query},StatusCode=HTTP_301}

After, we need to use modify-listener to edit default rule for HTTP:80 listener, interestingly. modify-rule documentation says

To modify the actions for the default rule, use ModifyListener .

aws elbv2 modify-listener
--listener-arn arn:aws:elasticloadbalancing:us-west-2:123456789012:listener/app/my-load-balancer/50dc6c495c0c9188/f2f7dc8efc522ab2
--default-actions Type=redirect,Order=1,RedirectConfig={Protocol=HTTPS,Port=443,Host=www.site.com,Path=/#{path},Query=#{query},StatusCode=HTTP_301}

Configuration works perfect for me, you can check yours by viewing rules using AWS CLI and comparing with mine.

HTTP:80 listener configuration with HTTP:80 listener ARN;

$ aws elbv2 describe-rules --listener-arn arn:aws:elasticloadbalancing:***

{
    "Rules": [
        {
            "Priority": "default",
            "Conditions": [],
            "RuleArn": "arn:aws:elasticloadbalancing:***",
            "IsDefault": true,
            "Actions": [
                {
                    "RedirectConfig": {
                        "Protocol": "HTTPS",
                        "Host": "www.***.com",
                        "Query": "#{query}",
                        "Path": "/#{path}",
                        "Port": "443",
                        "StatusCode": "HTTP_301"
                    },
                    "Type": "redirect",
                    "Order": 1
                }
            ]
        }
    ]
}

HTTP:443 listener configuration with HTTP:443 listener ARN;

$ aws elbv2 describe-rules --listener-arn arn:aws:elasticloadbalancing:***
{
    "Rules": [
        {
            "Priority": "1",
            "Conditions": [
                {
                    "Field": "host-header",
                    "Values": [
                        "***.com"
                    ]
                }
            ],
            "RuleArn": "arn:aws:elasticloadbalancing:***",
            "IsDefault": false,
            "Actions": [
                {
                    "RedirectConfig": {
                        "Protocol": "HTTPS",
                        "Host": "www.***.com",
                        "Query": "#{query}",
                        "Path": "/#{path}",
                        "Port": "#{port}",
                        "StatusCode": "HTTP_301"
                    },
                    "Type": "redirect",
                    "Order": 1
                }
            ]
        },
        {
            "Priority": "default",
            "Conditions": [],
            "RuleArn": "arn:aws:elasticloadbalancing:***",
            "IsDefault": true,
            "Actions": [
                {
                    "TargetGroupArn": "arn:aws:elasticloadbalancing:***",
                    "Type": "forward",
                    "Order": 1
                }
            ]
        }
    ]
}

On AWS Elasic Beanstalk

Since we did these configuration changes by using AWS Console or CLI, they won’t be applied when you re-deploy your app or environment. So, we need to prepare this configuration as a code an place it to the .ebextensions/ folder of your EB app. This approach gives a lot of opportunities to set your AWS resources and configurations for your Elastic Beanstalk app.

According to the AWS EB documentation, configuration of an application load balacer is possible with creating a file as .ebextensions/application-load-balancer.config in the eb app folder.

option_settings:
  aws:elasticbeanstalk:environment:
    LoadBalancerType: application

On Application Load Balancer Namespaces section, it’s stated that

aws:elbv2:listenerrule – Configure rules that route traffic to different processes, depending on the request path. Rules are unique to Application Load Balancers.

Also, on option configuration page;

To use a rule, add it to a listener with the Rules option in the aws:elbv2:listener:listener_port namespace.

Well, things are going a little complex here. This means, we need to add aws:elbv2:listenerrule:rule_name rule first, then we call them with aws:elbv2:listener:listener_port namespace. But, what about editing the default rule? I think we can modify listener’s itself to modify the rule but how to assign newly created aws:elbv2:listenerrule:rule_name rule to the listener? I feel that advanced configuration isn’t “elastic” enough since Elastic Beanstalk has own configuration schema and also CloudFormation has another. Every single feature in API’s is not supported for both of them.

When I find a workaround for this stuff, I’ll update the post. For now, use the console or CLI version, sadly. Preparing commands for AWS CLI and running them just after creating the EB environment solves the problem quickly. This gives more freedom if compared to EB config.

Please comment here when you find something about auto configuration of non-www and https redirects with AWS Elastic Beanstalk.