Redirecting Traffic from EC2 Public DNS to a Domain Name

301 redirect from hostname

I searched Google for my own website, and noticed that some results were returning with my EC2 instance’s public hostname: http://ec2-18-191-145-67.us-east-2.compute.amazonaws.com/ – That’s a bad look for branding, and I figured it couldn’t be good for SEO. I fixed it by adding a new rewrite condition to my apache server’s httpd.conf file. The server runs on a Linux 2 image, and I found that file in this directory: /etc/httpd/conf/.

Initial Configuration

I already had a Virtual Host block that listens on port 80, responsible for making sure all traffic going through my domain name goes to the https://www.antpace.com. I just had to add the public DNS to the ServerAlias statement and add an additional RewriteCond statement:

    
<VirtualHost *:80>
    DocumentRoot "/var/www/html"
    ServerName "antpace.com"
    ServerAlias "www.antpace.com" "ec2-18-191-145-67.us-east-2.compute.amazonaws.com"
    RewriteEngine on
    RewriteCond %{HTTP_HOST} ^ec2-18-191-145-67.us-east-2.compute.amazonaws.com$ [OR]
    RewriteCond %{SERVER_NAME} =antpace.com [OR]
    RewriteCond %{SERVER_NAME} =www.antpace.com
    RewriteRule ^ https://www.antpace.com%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

Handling SSL Traffic

After restarting the server, I did another search, and saw some pages still returning with the SSL encrypted address https://ec2-18-191-145-67.us-east-2.compute.amazonaws.com/.

SERP

To handle traffic going there, I needed to add another Virtual Host block that would listen on port 443. You’ll notice that I had to reference my SSL certificates that were installed via Let’s Encrypt.

<VirtualHost *:443>
    DocumentRoot "/var/www/html"
    ServerName "antpace.com"
    ServerAlias "www.antpace.com" "ec2-18-191-145-67.us-east-2.compute.amazonaws.com"
    
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/antpace.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/antpace.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/antpace.com/chain.pem
    
    RewriteEngine on
    RewriteCond %{HTTP_HOST} ^ec2-18-191-145-67.us-east-2.compute.amazonaws.com$ [OR]
    RewriteCond %{HTTP_HOST} ^antpace.com$ [OR]
    RewriteCond %{HTTP_HOST} ^www.antpace.com$
    RewriteRule ^ https://www.antpace.com%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

Unfortunately, adding this block broke the site temporarily. It resulted in a redirect loop, in tandem with the previous block. I tried to adjust them both, resulting in the following:

<VirtualHost *:80>
    DocumentRoot "/var/www/html"
    ServerName "antpace.com"
    ServerAlias "www.antpace.com" "ec2-18-191-145-67.us-east-2.compute.amazonaws.com"

    RewriteEngine on
    # Redirect all HTTP traffic to HTTPS
    RewriteCond %{HTTPS} off
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>

<VirtualHost *:443>
    DocumentRoot "/var/www/html"
    ServerName "antpace.com"
    ServerAlias "www.antpace.com" "ec2-18-191-145-67.us-east-2.compute.amazonaws.com"

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/antpace.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/antpace.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/antpace.com/chain.pem

    RewriteEngine on
    # Redirect all non-preferred hostnames to the preferred hostname
    RewriteCond %{HTTP_HOST} ^ec2-18-191-145-67.us-east-2.compute.amazonaws.com [NC,OR]
    RewriteCond %{HTTP_HOST} ^antpace.com [NC]
    RewriteRule ^ https://www.antpace.com%{REQUEST_URI} [L,R=301]
</VirtualHost>

Although this configuration resolved the redirect loop, it failed to address the original issue. I discovered that my system had a separate configuration file for SSL traffic: /etc/httpd/conf.d/ssl.conf. I added the redirect rule and certificate references to the 443 Virtual Host block within this file, but the redirect issue persisted.

Diagnosing the Issue

Executing sudo apachectl -S revealed a conflicting Virtual Host on port 443 in httpd-le-ssl.conf. I moved the rewrite rules to this file, yet the redirect remained elusive. Suspecting a browser cache issue, I verified the redirect using curl:

301 redirect via CLI CURL

The 301 Moved Permanently response confirmed the redirect to https://www.antpace.com. Since the redirection works when tested with curl -k but not in a browser, the issue likely stems from the browser not accepting the SSL certificate due to the mismatch. Browsers are more strict with SSL certificate validation than curl -k.

I attempted to add the public host name as a subject alternative name (SAN) in my existing SSL certificate, but Let’s Encrypt refuses to issue certificates for EC2 hostnames. This restriction is due to Let’s Encrypt’s policy to prevent abuse. To circumvent this, I generated a self-signed certificate:

sudo mkdir -p /etc/ssl/private 
sudo mkdir -p /etc/ssl/certs
sudo chmod 700 /etc/ssl/private
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/ec2-selfsigned.key -out /etc/ssl/certs/ec2-selfsigned.crt

Final Configuration

I created a new Apache configuration file for the EC2 redirection and ensured no conflicts with existing configurations:

sudo nano /etc/httpd/conf.d/ec2-redirect.conf

Here’s the contents:

<IfModule mod_ssl.c>
<VirtualHost *:443>
    DocumentRoot "/var/www/html"
    ServerName "ec2-18-191-145-67.us-east-2.compute.amazonaws.com"

    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/ec2-selfsigned.crt
    SSLCertificateKeyFile /etc/ssl/private/ec2-selfsigned.key

    RewriteEngine on
    # Redirect EC2 URL to the preferred domain
    RewriteRule ^ https://www.antpace.com%{REQUEST_URI} [L,R=301]
</VirtualHost>
</IfModule>

Upon restarting Apache, the 301 redirect functioned correctly in the browser, albeit with an initial security warning due to the self-signed certificate. Accepting the certificate led me to the desired URL. This project underscored the importance of meticulous configuration and validation in managing web traffic and ensuring optimal SEO performance.

Pro-tip: Each time I had to edit any of these sever configuration files, I did so from the command line using the Nano text editor. I learned along the way that I could select multiple lines of text by pressing “Ctrl + Shift + 6”, pressing the arrows to expand the selection, and then “Ctrl + K” to remove content.

Alternative Approaches

To stop this problem from arising, even to begin with, we can take an alternative approach. It is possible, when creating a new EC2 instance, to disable the auto-assign public IP address in the network settings. This will launch the instance without a public IP address.

Lazy Load Images and Assets on WordPress with IntersectionObserver

wordpress homepage design

I write online a lot. Adding articles to this blog serves to build a catalog of technical solutions for future reference. Updating the homepage has improved user experience and SEO. The new design displays the most recent articles as clickable cards, rather than listing the entire text of each one. The changes for this were added to index.php file, in the child-theme folder. The theme’s original code already used a While() loop to iterate through the post records. My modification removed the article content, and only kept the title and image:

<div class="doc-item-wrap">
	<?php
	while ( have_posts() ) {
		the_post();
		echo "<div class='doc-item'><a href='". get_the_permalink() ."'><img class='lazy' data-src='".get_the_post_thumbnail_url()."'><h2>" . get_the_title() . "</h2></a></div>";
	} ?>
</div> <!-- doc-item-wrap -->

I used custom CSS, leveraging Flexbox, to style and position the cards:

.doc-item-wrap{
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
}
.doc-item{
    width: 30%;
    padding: 20px;
    border: 3px solid #f0503a;
    margin: 15px;
    background: black;
    flex-grow: 1;
    text-align: center;
}
.doc-item:hover{
    background-color: #34495e;
}
.doc-item p{
    margin: 0px;
    line-height: 40px;
    color: white;
}
.doc-item img{
    display: block;
    margin: 0 auto;
}
.doc-item h2{
    font-size: 22px;
    color: white;

}
@media(max-width: 1000px){
	.doc-item{
		width: 45%
	}
}
@media(max-width: 700px){
	.doc-item{
		width: 100%
	}
}

The media queries adjust the size of the cards (and how many are in a row), based on screen size.

Look and feel of the design

Card layout design is a common way to arrange blog content. It gives visitors a visual overview of what’s available. It also stops the homepage from duplicating content that’s already available on the individual post pages.

You can see this pattern throughout the digital world. Card layout translates well across screen sizes and devices. Since I put much effort into writing, making it organized was a priority. This implementation can be extended to add additional content (such as date, description, etc.) and features (share links, animations, expandability). And, it fits nicely with what WordPress already provides.

Lazy loaded images

Image content can often be the biggest drag to site speed. Lazy loading media defers rendering until it is needed. Since this blog’s homepage has an image for each post, this was essential.

While iterating through post records the image URL is assigned to a custom data-src attribute on the image tag, leaving the normal src blank. This assures the image is not immediately retrieved nor loaded. I wrote a JavaScript function to lazy load the images, relying on the IntersectionObserver API. The card’s image does not load until a user scrolls it into view. This improves the speed of the page, which has a positive effect on SEO and UX.

The code creates a IntersectionObserver object.  It observes each of the image elements, checking to see if they are within the browser viewport. Once the image elements come into view, it takes the image URL from the data-src attribute, and assigns it to the tag’s src – causing the image to load.

document.addEventListener("DOMContentLoaded", function() {
  var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
  if ("IntersectionObserver" in window) {
    let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          let lazyImage = entry.target;
          lazyImage.src = lazyImage.dataset.src;
          // lazyImage.srcset = lazyImage.dataset.srcset;
          lazyImage.classList.remove("lazy");
          lazyImageObserver.unobserve(lazyImage);
        }
      });
    });

    lazyImages.forEach(function(lazyImage) {
      lazyImageObserver.observe(lazyImage);
    });
  } 
});

The original JS code was referenced from a web.dev article. Web.dev is a resource created by Google that provides guidance, best practices, and tools to help web developers build better web experiences.

You can also use this same method for lazy loading videos, backgrounds, and other assets.

IntersectionObserver to lazily load JavaScript assets

I discovered another application for the IntersectionObserver implementation that I used above to load lazy load images. The Google Lighthouse performance score on my homepage was being dinged due to the “impact of third-party code”.

impact of 3rd party js on performance scores

The largest third-party resource was is used for the reCaptcha on a contact form at the bottom of my homepage. It makes sense to not load it until the user scrolls down to that section – especially because the UX expects them to take time to fill out the form before hitting “submit” anyway.

Using the same pattern as above, I created a new `IntersectionObserver` and passed the contact form section as the target:

function captchaLazyLoad(){
	contactCaptchaTarget = document.getElementById('contactSection')
	if (!contactCaptchaTarget) {
        return;
    }
	let contactCaptchaObserver = new IntersectionObserver(function(entries, observer) {
		if (entries[0].isIntersecting) {
            var script = document.createElement('script');
		    script.src = "https://www.google.com/recaptcha/api.js";
		    document.body.appendChild(script);
            contactCaptchaObserver.unobserve(contactCaptchaTarget);
        }
	})
	contactCaptchaObserver.observe(contactCaptchaTarget);
}

I included this function to the already existing `DOMContentLoaded`  event listener just below the loop to observe lazy image elements:

<script>
function captchaLazyLoad(){
	contactCaptchaTarget = document.getElementById('contactSection')
	if (!contactCaptchaTarget) {
        return;
    }
	let contactCaptchaObserver = new IntersectionObserver(function(entries, observer) {
		if (entries[0].isIntersecting) {
            var script = document.createElement('script');
		    script.src = "https://www.google.com/recaptcha/api.js";
		    document.body.appendChild(script);
            contactCaptchaObserver.unobserve(contactCaptchaTarget);
        }
	})
	contactCaptchaObserver.observe(contactCaptchaTarget);
}

document.addEventListener("DOMContentLoaded", function() {
  var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
  if ("IntersectionObserver" in window) {
    let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          let lazyImage = entry.target;
          lazyImage.src = lazyImage.dataset.src;
          // lazyImage.srcset = lazyImage.dataset.srcset;
          lazyImage.classList.remove("lazy");
          lazyImageObserver.unobserve(lazyImage);
        }
      });
    });

    lazyImages.forEach(function(lazyImage) {
      lazyImageObserver.observe(lazyImage);
    });

    //
    captchaLazyLoad()

  } else {
    // Possibly fall back to a more compatible method here if IntersectionObserver does not exist on the browser
  }

});

</script>

This update raised my Lighthouse performance score by fifteen points!

Building my career in tech as a programmer

Anthony Pace's resume and portfolio

Building a fulfilling career can seem daunting. Technology and programming is a great option in today’s world. Resources and opportunities are abundant. You can work from anywhere and help build the future. When I started out, I faced challenges, doubt, and struggle. The ride has been worth it, and I’m excited to keep moving forward.

Starting out

About half way through college, I decided to dropout. I was majoring in Philosophy at a small school in New York.  My main source of income was delivering pizza in the Bronx.

A decade earlier, I found computer programming. I spent my nights coding desktop applications, learning HTML, and exploring the web. Those early days of technology laid the foundation for what would be my career.

When I left school in 2007, I wasn’t sure what to do next. I started earning money in tech that same year by starting a business. It focused on creating blogs and producing content. Ads and affiliate programs served to generate revenue.

It wasn’t as lucrative as I hoped. The real value came from the web development skills I honed. The software and technologies I used then, I still rely on today.

WordPress, Linux, and PHP. Writing, SEO, and digital marketing. These were the bricks I used to form the ground floor of my career in tech.

Service worker

While my early stint at entrepreneurship didn’t make me wealthy, it proved valuable. I managed to grow a freelance business leveraging this experience.

Networking and word-of-mouth were my primary means of growth. After printing business cards, I would give them to everyone I met. While delivering pizzas, I would hand them out to any small businesses or shops I passed.

I found my first paying customer in 2008. Since then, my client list has grown to triple digits.

The services I’ve offered range beyond web development. I’ve designed logos and written copy. I’ve managed infrastructure: web hosting, domain names, email, and more.

I have designed and managed both print and digital marketing campaigns. I’ve given strategy advice to young startups. Truly full stack: business, technology, and design. This has been a theme that has rung true my entire career.

The lessons learned during this period were ones of hard-work and getting the job done. The most valuable skills translate across industries. Finding clients fuels the engine of any business. The art of pitching and selling is a career-long study. Being able to manage business needs has proven to be foundational.

Office life

By 2011 I landed my first in-house gig, working at a marketing company. It felt like a turning point. I was the only developer, and got to deal directly with clients. I worked there for less than a year.

In 2012 I connected with a recruiter for the first time. They set me up on many interviews. I clicked with a small medical education company based in Manhattan. Hired as a web developer, I graduated to senior engineer and marketing specialist.

Team work

There, I was the head of all things digital. That meant building websites, coding native apps, and managing infrastructure. After a promotion to head of marketing my responsibilities expanded. Managing analytics took time. Copywriting promotional materials required patience. My horizons expanded while coordinating live events, and traveling internationally to exhibition shows.

Educational grants funded our projects. They included apps, websites, live events, and digital newsletters. Having a coordinated team was imperative to making things work. The project management and leadership was world-class and invaluable.

A single project was multifarious. I would design responsive layouts, build registration websites, deploy apps, and more. Once a product would launch, I would travel to live events to handle promotion and logistics. While I fulfilled many roles, I was lucky to work with a talented group.

Software Engineer

After four years, I made the difficult decision to leave the job that helped shape my career. A better opportunity presented itself in 2016. I was hired as a software engineer. This is when I came into my own as a programmer. I was able to collaborate with a brilliant team. The technologies I became familiar with continued to grow.

I got to work with early-stage startups and brands backed by venture capital. I learned the intricacies of building digital products and growing direct-to-consumer brands. My colleagues included entrepreneurs, CEOs, and product experts. The office was exciting and full of talent.

At the time of writing this (2020), we are stuck in quarantine due to the COVID-19 pandemic. We’re working remotely, but continuing to grow. Uncertain times prompt us to evaluate our circumstances and take inventory of what we value. What is the future of my career? How does it play into my life overall?

What’s next?

I love what I do for a living. I enjoy programming; I love problem solving; I’m an artist at heart. I plan on continuing to build software products. Chances are, I’ll be doing it somewhere other than New York City – especially since remote work seems to be the future of business.

If you’re thinking about starting a career in technology as a programmer, my advice is to jump right in. Start building, keep learning, and put yourself out there. If anyone reading this wants to chat about careers, technology, programming, or anything else, feel free to email me!