Programmatically Convert a Large Static HTML Website Into One with Header and Footer Partials

Let’s say you’ve inherited a large website that uses some home-grown static site generator (SSG) and there’s no documentation. Your build and release infrastructure is fragile and also custom. Your git repo is massive with content from two decades, including lots of binary files. You want to migrate this massive piece of shit to a popular SSG like Eleventy and you want to use a reliable deployment system like GitHub + Netlify. Let’s say you can’t migrate all source files because there’s no easy way to do so between your custom SSG and Eleventy. If you’re willing to sacrifice most of your layouts and partials (includes) and just migrate everything all of the built static files to Eleventy with one partial for the header and one for the footer, then here’s one way to do it.

1. Copy Your Static Site to a New Folder

If you don’t have access to the SSG and the web servers, you can download the whole website from the internet using wget.

If your website is on WordPress, install the Simply Static plugin and export your site to a zip file (free).

2. Copy the Header and Footer Code Blocks to Separate Files

Let’s say your HTML looks something like this.

<html>
<head>
	...
</head>
<body>
<header class="header">
	<div>Logo</div>
	<ul>
		<li>Link 1</li>
		<li>Link 2</li>
	</ul>
</header>
<section>
	<p>Hello, World!</p>
</section>
<footer class="footer">
	<p>Copyright 2024</p>
	<div>blah blah blah</div>
</footer>
</body>
</html>

In this case, you can create separate files like this:

/includes/header.njk

<header class="header">
	<div>Logo</div>
	<ul>
		<li>Link 1</li>
		<li>Link 2</li>
	</ul>
</header>

/includes/footer.njk

<footer class="footer">
	<p>Copyright 2024</p>
	<div>blah blah blah</div>
</footer>

3. Search and Replace the Header Code Block with a Include

For simplicity, let’s say that your HTML looks something like this:

<html>
<head>
	...
</head>
<body>
<div class="header">
	<div>Logo</div>
	<ul>
		<li>Link 1</li>
		<li>Link 2</li>
	</ul>
</div>
<section>
	<p>Hello, World!</p>
</section>
<div class="footer">
	<p>Copyright 2024</p>
	<div>blah blah blah</div>
</div>
</body>
</html>

If your header and footer code blocks don’t use unique HTML tags like “header” and “footer”, then you may have a problem searching and replacing these code blocks. For example, in VS Code, if I try to select the header block beginning with <div class="header">, I can’t do so due to the nested div tag.

Using the regex

<div class="header"(.|\n)*?</div>

notice how the selection ends prematurely at the closing nested div tag. In this situation, you can update your source code to replace the open and closing div tags with the standard <header> tag. You can do the same with the footer by using the <footer> tag. After updating the source code, you can rebuild your static HTML pages and then use a regex like

<header(.|\n)*?</header>
<footer(.|\n)*?</footer>

to search and replace the header and footer code blocks with a code reference that includes those code blocks using whatever template engine you want to use.

If you want to use the Nunjucks template engine, for example, then you can replace those code blocks with something like

{% include "header.njk" %}
{% include "footer.njk" %}

4. Rename file extensions

Rename all HTML files so their extensions are .njk instead of .html.

brew install rename
find . -name "*.html" -exec rename 's|\.html|\.njk|' {} +

5. Install an SSG

Create a new folder and install an SSG. In this case, I’ll install Eleventy.

mkdir mysite 
cd mysite
npm init -y
npm install --save-dev @11ty/eleventy

Move your website files to your new Eleventy project. To follow Eleventy’s default conventions, your folder structure should look something like this.

Note that we put the header and include partials in the “_includes” folder under the “src” folder. Therefore, our header and footer include references should be updated to look like this

<html>
<head>
	<title>Home Page</title>
</head>
<body>
{% include "src/_includes/header.njk" %}
<section>
	<p>Hello, World!</p>
</section>
{% include "src/_includes/footer.njk" %}
</body>
</html>

6. Test

If you don’t create an Eleventy config file, then Eleventy will use all of its defaults and output built files to a “_site” folder and it will build the partials as well.

Since we don’t want to build the partials, let’s create an Eleventy config file.

7. Create an Eleventy config file

In the project root, create a file called .eleventy.js with the following content.

module.exports = function(eleventyConfig) {
	eleventyConfig.addPassthroughCopy("src", {
		//debug: true,
		filter: [
			"404.html",
			"**/*.css",
			"**/*.js",
			"**/*.json",
			"!**/*.11ty.js",
			"!**/*.11tydata.js",
		]
	});
  
	// Copy img folder
	eleventyConfig.addPassthroughCopy("src/img");

	eleventyConfig.setServerPassthroughCopyBehavior("copy");

	return {
		dir: {
			input: "src",
			// ⚠️ These values are both relative to your input directory.
			includes: "_includes",
			layouts: "_layouts",
		}
	}
};

If you rerun Eleventy, you’ll see that the partials are not built and copied to the output folder.

8. Create a layout (optional)

If you want your page content to be wrapped in other content, you can create a layout. This is called template inheritance. Both Nunjucks and 11ty have their own template inheritance mechanism. With Nunjucks, you inherit a parent template using 

{% extends "parent.njk" %}. 

With 11ty, you inherit a parent template using front matter, e.g.

---
layout: parent.njk
---

Nunjucks supports template blocks natively, but it doesn’t support front matter. 11ty supports front matter, but it doesn’t support template blocks natively. Learn more about creating a layout using 11ty’s template inheritance mechanism.

9. Change absolute links to relative ones

If you have absolute links to the same site, you should replace them with relative ones, e.g.

https://www.mysite.com/products/ -> /products/

You can do this easily in VS Code’s search and replace feature.

10. Move all images to AWS S3 and an image CDN like ImageKit

If your website images are local, I would move them to AWS S3 and then use the S3 URL as a custom origin to pull the images into an image CDN like ImageKit or Cloudinary. If your site is in WordPress, the images will be in the wp-content/uploads folder. You can then do a global search and replace to reference the image CDN URL.

Download a Website Using wget

Recently, I needed to clone a website and make a few minor changes to it. I wanted to publish a slightly modified copy of the website. Luckily, it’s easy to do that using wget. Here’s how I did it.

1. Install wget

I’m on Mac, so I installed wget using Homebrew using the command

brew install wget

2. Download site

I wanted to download this small website. I used this command:

wget -p -r https://events.govexec.com/qualys-cyber-risk-conference/
  • The -p flag means download all page requisites, such as images, stylesheets, etc.
  • The -r flag means recursive.

Learn more

3. Search and replace

Since I downloaded a bunch of HTML files, if I wanted to replace a common element on multiple pages, the easiest way was to do a search and replace. Using VisualStudio Code, you can easily find all HTML blocks within a particular tag using a multi-line regex. Here are some example regexes:

<footer(.|\n)*?</footer>
<script(.|\n)*?</script>
<a class="popup(.|\n)*?</a>

Note: these regexes only work if the tags don’t have any nested tags with the same name.

Video Codec VS. Container

With so many video codecs and containers, it’s easy to get confused. Here’s a simple explanation.

Codec (Video Format)

Codec stands for coder-decoder. Video codecs are algorithms for encoding and decoding video data.

  • An encoder compresses video streams, which reduces the amount of data for storage and transmission.
  • A decoder reverses the conversion for playing or editing a video stream.

For simplicity, you can think of a video codec as the video format.

Examples of video codecs are H.261, H.263, VC-1, MPEG-1, MPEG-2, MPEG-4, AVS1, AVS2, AVS3, VP8, VP9, AV1, AVC/H.264, HEVC/H.265, VVC/H.266, EVC, LCEVC

Currently, the most popular codec is AVC/H.264.

Container (File Format)

With respect to video, a container is a data storage. It can include compressed video and audio sequences, subtitles, service information and metadata. It is a package or bundle.

For simplicity, you can think of a media container as the file format.

Examples of media containers are MPEG-1 System Stream, MPEG-2 Program Stream, MPEG-2 Transport Stream, MP4, MOV, MKV, WebM, AVI, FLV, IVF, MXF, HEIC

Currently, the most popular container is MP4.

Easily Upscale & Enhance Photos Using Google Photos and Topaz Photo AI

There are many ways and tools you can enhance a photo. If you’re a professional photographer, then you’ll likely have advanced methods, but for the average person, you’ll probably just want some quick and easy solutions. Like most average people, my photos are mostly taken from my phone (currently, Google Pixel 8). However, when vacationing, I also take a lot of video using my Insta360 X3 camera, and I’ll occasionally want to take snapshots of a video frame to add to my photo collection. With this in mind, here’s my current (simple) workflow for upscaling and enhancing photos.

Enhance a Photo Using Google Photos

First, upload your photo to Google Photos. Then, use one of the presets to enhance the photo. Here’s an example photo without any enhancements applied.

The average person might that the photo looks fine, but it can significantly be improved. Here’s how the photo looks when you click on each of the suggested improvement options.

Enhance
Dynamic
Portrait
Color Pop

Note that Color Pop tried to isolate the subject and convert everything else to grayscale. It’s not perfect because the subject’s right arm is partially gray. To fix this, you could select the subject in Photoshop either manually or automatically, invert the selection, and convert the selection to grayscale.

B&W Portrait

Now, let’s say that you have a heavily underexposed (dark) photo. (Learn more about under and overexposed photos).

Original

If you click the “Enhance” or “Dynamic” options, you’ll get this.

Enhance
Dynamic

For comparison, here are the photo’s input levels in Photoshop.

If I were to manually correct the exposure in Photoshop, this is what I’d get.

The photo is significantly improved, but it doesn’t look exactly like it does using the Google Photos presets.

If the Google Photos presets don’t look good enough, you can make many adjustments in the Settings tab. In the example below, I started by choosing the “Dynamic” preset, and then in the Settings tab, I increased the brightness.

So, for the average person, using Google Photos to improve photos is easy and usually adequate.

Enhance a Photo Using Topaz Photo AI

Topaz Photo AI can do many things to a photo, including

  • remove noise
  • sharpen
  • adjust lighting
  • balance color
  • recover faces
  • preserve text
  • upscale

You can also just run autopilot and let Topaz choose settings for you.

For me, I mainly use Topaz to enlarge (upscale) photos, remove noise, which can result from adjusting the levels of a heavily underexposed photo, and to sharpen photos. These improvements are particularly useful when I take a snapshot of a 1920×1080 video frame. For example, here’s a frame from a video.

I want to zoom in on the subject, crop it, enlarge it, and enhance it. Here’s the zoomed-in part cropped. The dimensions are 1048 x 589 px.

Now, I’ll drag it into Topaz and run autopilot to upscale and enhance the photo. It will take a minute to process. Here’s how the photo looks enlarged by 34% before enhancing it with Topaz.

Before Topaz

Here’s how it looks with Topaz enhancements applied.

After Topaz

There is a difference, but it will be more obvious when you zoom in. Below is a comparison zoomed in at 67% before and after using Topaz.

Before Topaz
After Topaz

At this point, you can copy the upscaled and sharpened photo from Topaz and paste it into Google Photos to enhance it.

Topaz Photo AI isn’t perfect, but depending on the original photo, it can often product amazing results.

Upscale an Image Using AI

If you have old, low-res photos that you want to enhance and upscale or if you want to zoom in on a hi-res photo while preserving quality, you’ll be impressed with what artificial intelligence (AI) can do. Compare the following.

Original Photo

This photo was taken in Cairo, Egypt back in 1997. The original photo was 640 by 480 pixels. I’ve cropped it to focus on the subject. It’s now 238 px wide.

Photoshop

In Photoshop, you can increase the dimensions of an image. I’m going to enlarge it by 300% to 714 px wide.

Here are the results using the “Automatic” resampling option. Notice the graininess.

Now, I’ll do the same using the “Preserve Details (enlargement)” option with a 50% noise reduction.

Here are the results. It’s less grainy, but still not sharp at all.

I’ll try one more time. Below are the results with 100% noise reduction. Still not great.

Spyne AI Image Enhancer

Let’s see how well Spyne AI image enhancer does.

Here are the results. This is definitely an improvement compared to Photoshop.

Topaz Labs Photo AI

Now I’ll try Topaz Labs Photo AI 2.4.0. This software costs $200, so I’ve just taken a screenshot of the preview. As you can see, the results are way better than both Photoshop and Spyne. There is no noise and everything is sharp, including the hair. If the face looks a bit too soft, you can sharpen it in Photoshop under Filter > Sharpen.

So there you have it. Successfully upscaling an image using AI with realistic results.

Brighten a Face Using Photoshop

Chances are you’ve taken photos where the faces are too dark. This can be due to a low-quality camera and/or incorrect camera settings. Fortunately, there is a way to easily fix this using Photoshop. As an example, I’ll use this photo of me in the Wynwood district of Miami (below). Notice how my face lacks detail because it’s underexposed (too dark).

Levels

First, I like to check and adjust the levels for the image. Go to Image > Adjustments > Levels. You’ll see a histogram like this

In the input levels, there are 3 markers from left to right

  • black marker (left) = shadows
  • gray marker (middle) = midtones
  • white marker (right) = highlights

The left and right markers should touch the left and right edges of the histogram. In this case, there is a small gap between the right marker and the right edge of the histogram, meaning the image is a little underexposed. If we move the right marker to the right edge of the histogram, the image will become a little brighter.

In this particular example, this adjustment isn’t significant. But, if there’s a big gap in the histogram like this

then adjusting the levels will make a big improvement to your image.

Exposure

If adjusting the levels doesn’t brighten your subject’s face enough, you can adjust the exposure of just the face. First, make a circular selection around a face.

Then, add a feather to it. Select > Modify > Feather. For a 1920 x 1080 image, I create a 50-pixel feather.

Then, adjust the exposure by going to Adjustments > Exposure

You will see a slider for Exposure. Drag it to the right to increase the exposure.

Notice how the face is brighter.

If I increase the exposure too much, the face will look nice and bright, but it won’t look natural against the darker surroundings.

The problem is there appears to be a white glow around the face. In this case, I can select just the subject or the subject’s face to limit the exposure adjustment area. In this case, no feather is needed.

Now, only the subject is brightened.

I can also tweak the levels by adjusting the shadows and the midtones so that the subject doesn’t look unnaturally bright against a darker background.

Before and After

As you can see in the before and after image below, my face looks much brighter while still appearing natural.

Fundamental SEO Misunderstandings & Tips

Many people wonder why their website doesn’t rank high for a particular keyword. Let’s take cybersecurity as an example because I am familiar with that space. A cybersecurity company usually has products and events, and a need to market them. One type of product is a tool to manage vulnerabilities. The industry refers to the topic as “vulnerability management” or “VM”. Marketers would then often ask, “why isn’t my VM product page showing up at the top of Google when people search for “vulnerability management”. Likewise, if the company has an annual user conference with session talks about cybersecurity, marketers will again often ask why their conference website doesn’t show up for the keyword “cybersecurity”. The main reason lies in search intent.

Search Intent

When people search for “vulnerability management” or “cybersecurity”, they are most likely not searching for a VM product or a cybersecurity conference. These generic terms likely indicate that they are searching for information about them rather than a tool or a conference. Consequently, search results for those terms show informational pages containing content explaining what they are as if a user searched for “what is vulnerability management” or “what is cybersecurity”.

There are 4 types of search intent:

  • Navigational intent: Users want to find a specific page (e.g., “reddit login”)
  • Informational intent: Users want to learn more about something (e.g., “what is seo”)
  • Commercial intent: Users want to do research before making a purchase decision (e.g., “best coffee maker”)
  • Transactional intent: Users want to complete a specific action, usually a purchase (e.g., “buy subaru forester”)

Therefore, for the VM tool and cybersecurity conference examples above, the correct search terms to check would be something like

  • (best) vulnerability management tool
  • cybersecurity conference

The qualifiers “tool” and “conference” make it clear what people’s search intent is so they find content relevant to what they are actually looking for. But how do you know what keywords people are searching for? This is where keyword research tools come in.

Keyword Research

To do keyword research, you can use a free tool like Google Keyword Planner or a paid tool like Semrush. I’ll use Google Keyword Planner. You can use it to

  • discover new keywords and
  • get keyword search volume and forecasts.

Continuing with our example, let’s see how many people search for the following keywords:

  • vulnerability management
  • vulnerability management tool
  • cybersecurity
  • cybersecurity conference

I enter those keywords as shown below…

and Google spits out the following data

The keyword “vulnerability management” gets many more searches per month compared to “vulnerability management tool”. But, since people searching for “vulnerability management” are probably not looking for a tool or software, people will likely ignore your VM product page even if it does show up in the #1 spot. The keyword “vulnerability management tool” gets fewer searches, but ranking for it will at least match your product page with a targeted group of interested people. Therefore, you’ll want to optimize your page for the keyword “vulnerability management tool”, e.g. by adding the word “tool” to your page copy, and monitor your Google page rank for that keyword.

As for the conference example, though the keyword “cybersecurity” gets a huge number of searches per month, it’s such a generic term that most people are probably not looking for a cybersecurity conference when they search for “cybersecurity”. On the contrary, the keyword “cybersecurity conference” is more specific / targeted and gets a decent search volume. Therefore, that is the term you should optimize for and monitor your page rank for.

In addition to the keywords you may already know about, you can using Google Keyword Planner to discover other relevant keywords. For example, if I enter “vulnerability management tool”…

I get the following results.

It looks like some companies like “Qualys” and “Rapid7” are known for having vulnerability management tools, so people looking specifically for their respective product pages just use the company name as a qualifier. However, there are some non-brand keywords that get a decent amount of searches as well, including “vulnerability management program” and “vulnerability management process”. While these keywords can be included in a VM product page, they may deserve to have their own pages.

How Long to Rank High

Now that you know what keywords to target, another misunderstanding people have is that adding keywords to a page will result in a higher page rank in just a matter of weeks. First of all, keywords are just one of many factors that affect page rank. SEO is a long-term strategy. Unless you’re looking to rank high for a long-tail keyword – a keyword so specific that there is very little competition – don’t expect immediate results.

Remove an Object, Text or Watermark from a Video Using Adobe After Effects Content-Aware Fill

The video below has some text added to it.

Video with overlaid text / watermark

The video below is the same video but with the text removed.

Same video with text / watermark removed

We can remove objects like text and watermarks from videos using Adobe After Effects (AE) content-aware fill feature. Here’s how I did it for the video above.

1. Open your video in Adobe After Effects

Choose “New Composition From Footage” and select your video.

2. Shorten work area

The content-aware fill takes a long time to process. To speed up testing, shorten the work area to a 2-second section by dragging the blue start and end markers on the timeline.

3. Create a mask

Click a tool like the pen tool and create a mask area around the object (in this case, text) you want to remove.

4. Adjust mask settings

In the video track, expand the Masks and select “Subtract”. For the Mask Feature, choose 20 pixels for the vertical and horizontal feather. For the Mask Expansion, choose 20 pixels as well. Play with these settings until you find values that produce good results.

These changes cause the mask to look like this

5. Adjust content-aware fill settings

If the content-aware fill pane isn’t open, open it by going to Window > Content-Aware Fill.

For the alpha expansion, I set the value to 13. You can experiment with different values.

For the fill method, choose “Object”.

For the range, choose “Work Area” because we only want to apply the fill to the short 2-second work area for now.

Click the “Generate Fill Layer” button. You will be asked to save the project if you haven’t already done so. You will then see AE analyze and generate the fill layer.

You will see the fill layer above the video layer in the list of tracks / layers.

6. Preview the content-aware fill layer

Click the play button in the preview pane to preview the content-aware fill.

This is how it looks for me. I think that looks good. If it doesn’t look good, go back and tweak some of the previous settings and try again.

7. Apply content-aware fill to the entire range containing the text to remove

Move the blue start and end work area markers to the beginning and end where the object / text you want to remove.

Disable the test content-aware fill layer by toggling the eye icon.

In the content-aware fill pane, click “Generate Fill Layer”. AE will create a new content-aware fill layer above our test layer. If the work area duration is long, this will take time to process.

Content-are fill will go through an analyzing phase followed by a rendering phase.

8. Preview complete content-aware fill effect

Click the play button in the preview pane again to see how the fill effect looks in the entire clip. If it looks good, you can export the video.

9. Export the video

Click File > Export > Add to Render Queue.

If the output location is unspecified, choose an Output To location. Then click the Render button.

Update: Use a reference frame for better results. The more reference frames, the better the results.

Create an Animated Travel Map Using Keynote

In this post, I’ll show you how to make an animated travel map like the one below using Apple Keynote.

1. Get an image of a map

I usually just go to Google Maps, zoom in/out to the area I want to show, then take a screenshot. In this example, I took a screenshot of the USA because I want to show an animated flight path from San Francisco to Miami.

2. Crop map and optionally add labels

Open the screenshot in an image editor (I use Photoshop) and crop to your target video resolution. My target resolution is 1920 x 1080 (standard HD). I also added some red dots where the start and end points will be as well as some city labels.

3. Get a transparent image of a plane, car, train, boat, etc

Since I want to show an airplane animate along a path, I looked for an image of one in Google Images. The background should be transparent. In Google Images, you can choose Tools > Color > Transparent to find images on a transparent background.

I chose this image.

4. Create a blank Keynote presentation

Open Apple Keynote and choose the basic white theme.

You will get a single slide. Select and delete everything in the slide.

5. Insert background map

Go to Media > Choose and select the background map.

6. Draw a path

Go to Insert > Line > Draw With Pen and draw your travel path.

Click on the start point then click on the end point. You will get a straight line.

In the middle of the line, there will be a point. Click and drag it up if you want to create a curve. Repeat with other midpoints as necessary.

When you’re done, hit the ESC key. We now have our travel path. Let’s change the style of the path. I’m going to make it red and thick. In the right pane, under Format > Style, you can edit the style of the element (curve). I choose a red color that is 7 pt thick.

7. Animate the path

In the top right corner, choose the Animate tab and then “Add an Effect” > “Line Draw”.

You can then change the default animation from 2 seconds. I changed the duration to 10 seconds so that in my video editor, I can slow it down without it appearing jumpy. I also changed the acceleration to “None”.

Click the “Preview” button to preview the path animation.

8. Add the airplane image

As in step 5, go to Media > Choose and select the airplane image.

Scale the airplane by dragging one of the corners. Drag the airplane to position it at the start point.

Rotate the airplane. In the top right choose Format > Arrange and adjust the rotation value such that the nose of the plane is aligned with the flight path.

9. Animate the airplane

In the top right, click Animate > Action > Add an Effect > Move.

Drag the airplane to the end point. Set the duration and acceleration to match that of the flight path (10 sec, None).

Click Preview to preview the animation. The airplane doesn’t yet follow the flight path. Check the “Align to path” checkbox. A point will appear along the line between the airplane’s start and end points. Drag that middle point to where the flight path is.

Click Preview again. You will see the airplane animate along the flight path.

10. Animate the flight path and airplane at the same time

In the top right, click Animate > Build Out > Build Order.

You will see a list of all animation effects. The first animation is the line (flight path). The second is the plane. Choose te second animation and then under “Start”, select “With Build 1”.

11. Export the animation

Choose File > Export To > Movie.

Since there’s only 1 slide, you can leave “Slides” to “All. The resolution should match that of the background image (1080p).

Set Up a Static Website Using AWS EC2, Apache and GitHub

Launch an EC2 Instance

  • Log in to AWS and go to EC2 > Instances > Launch an Instance
  • Enter a name. I’m calling mine “My Web Server”.
  • For Application and OS Image, I’ll just choose the default, which is “Amazon Linux 2023”.
  • For Amazon Machine Image (AMI), I’ll choose the default, which is “Amazon Linux 2023 AMI”
  • Under Key Pair, click “Create new key pair”.

Amazon EC2 can easily create a key pair for you. Just enter a key pair name. I chose “aws-ec2”. OpenSSH is available on Linux, Mac, and Windows 10+, so keep the default key format of .pem. On Windows 10+, OpenSSH is an optional feature you must install. Click the “Create Key Pair” button. The private key will be downloaded to your computer. Keep it in a safe place. You will need it to SSH into your EC2 instance.

In the Network Settings section, since we want to SSH into the EC2 instance and we want to be able to browse our website over HTTP and HTTPS, check those checkboxes.

Leave everything else at their defaults. Review the summary and click the “Launch Instance” button.

You will then see your EC2 instance listed. Wait for the “Status check” to change to “2/2 checks passed”.

Once your instance has been set up, click the button to connect to the instance.

SSH into EC2 Instance

You have a few options to connect to the EC2 instance. For simplicity, choose EC2 Instance Connect. This will open a new browser tab with shell access. Leave the default username as “ec2-user”.

You’ll notice the command prompt changes to ec2-user@ip-172-31-47-114 which is my default username (ec2-user) followed by my EC2 instance’s private IP (ip-172-31-47-114).

Install Apache

Since we installed Amazon Linux 2023, follow these instructions to install Apache. Since we don’t need MySql and PHP, ignore the commands and instructions for those. For example, instead of

sudo dnf install -y httpd wget php-fpm php-mysqli php-json php php-devel

just do

sudo dnf install -y httpd

Make sure to follow the instructions to set the file permissions so that Apache can serve the website.

  • The Amazon Linux Apache default document root is /var/www/html
  • The Apache config is at /etc/httpd/conf/httpd.conf
  • The Apache logs are in /var/log/httpd/

To view Apache errors, run the following command

sudo tail -100 /var/log/httpd/error_log 

Test that Apache works by going to the public IP address WITHOUT “https”, e.g. http://34.229.240.7/.

Set Up SSL/TLS

These instructions show how to create a self-signed certificate and a CA-signed certificate. For a self-signed cert, you don’t need a domain name. You can access your website over https by IP address, e.g. https://34.229.240.7/

For a CA-signed cert, you can follow these instructions to automate certificate renewals using Let’s Encrypt with Certbot. You can also use AWS Certificate Manager to manage and automatically renew certs.

Get a Fixed IP Address

The default IP address that AWS gives you is dynamic (will change whenever the server restarts). To get a static (fixed) IP address, get an Elastic IP Address. Once you get one, try to access your website over https, e.g. https://35.173.7.249/

Put Your Website in a GitHub Repo

  1. Create your website locally in a folder. If you have an existing website under git version control with a lot of history and you want to remove the history, git clone the repo into a new folder, delete the “git” folder, and then run git init.
  2. Create a new repo in GitHub
  3. Push your local repo to GitHub
git remote add origin git@github.com:...
git push -u origin

Clone the GitHub Repo to Your EC2 Server

  1. In your EC2 CloudShell (web-based terminal), install git (sudo dnf install git -y). Learn more.
  2. Generate a new SSH key and add it to the ssh-agent. Since your EC2 instance is Linux, follow the instructions for Linux.
  3. Add your new SSH key to your GitHub account
  4. Clone your GitHub repo to your EC2 instance. I’m going to clone it to my home folder.
  5. In GitHub, get the SSH URL of your repo.

Then, in your home folder, clone it

You will then see a new folder containing your website files from the GitHub repo.

Since my website document root is at /home/ec2-user/my-website/www, we need to update the Apache default document root (/var/www/html) to reference that path by editing the Apache config.

sudo nano /etc/httpd/conf/httpd.conf 
  • Change all references of /var/www/html to /home/ec2-user/my-website/www 
  • Change all references of /var/www to /home/ec2-user/my-website 
  • Restart Apache (sudo systemctl restart httpd)

Update Folder Permissions

If you try to view your website, e.g. by going to https://35.173.7.249/, you will probably get a “Forbidden” error. To better understand this error, view the Apache error log.

sudo tail -100 /var/log/httpd/error_log 

You will probably see an error like this

[Sat Dec 23 01:36:55.545345 2023] [core:error] pid 89394:tid 89446 Permission denied: [client 135.125.246.189:49368] AH00035: access to / denied (filesystem path '/home/ec2-user/my-website') because search permissions are missing on a component of the path

To fix this, follow these instructions on how to update file permissions

But replace /var/www with your website document root. In my case, I changed it to /home/ec2-user/my-website

According to the Apache docs, you also need to change the file permissions of all parent folders. In my case, the following folders need to be executable

  • /home
  • /home/ec2-user
  • /home/ec2-user/my-website
ls -la 
chmod +x . 
cd .. 
# repeat up to the root

When you’re done, you should be able to view your website in a browser.

Optional