WordPress Subdomain on Amazon with Let’s Encrypt

Install WordPress as a Subdomain on Amazon Linux with Let’s Encrypt SSL

This is the only end-to-end description I know that shows how to install WordPress as a subdomain on Amazon Linux EC2 instances.

Various articles reference Route 53, using configurations we actually do not need, or some examples of configuring SSL or Apache2 that do not fit the Amazon system.

You should have familiarity with installing Linux on Amazon and problem solving skills. Not for the feint of heart.


An overview – a vanilla installation

The example below will use my junk domain name, shawlw.me. This is usually not configured or online so the values shown here will not matter in terms of privacy or hacking.

We wish to end up with a primary WordPress site, in this example https://shawlw.me, and a second WordPress site under a folder called “aws” as a subdomain. This means https://shawlw.me/aws will end up as https://aws.shawlw.me.

I have not tested two separate instances, one as a supposed subdomain. I would wonder why in that case you would not register a domain name by itself. e.g. register subdomain.yourdomain.com and avoid subdomain configurations. I’m not sure subdomain configs would work as the whole idea of the subdomain on the same instance is to have a subfolder and configurations in httpd.conf and SSL to point Apache to the subdomain’s root folder.

We will firstly configure two hosted zones on Amazon Route 53, a primary domain and a subdomain. I have other articles which show further details such as adding SES configurations. I have not tested SES on a subdomain but see no reason why there would be an issue.

We will leave it to you to install one EC2 instance (see my other articles as needed), but take note of the steps below…

We must then install a system as http:// only, not https://. After the subdomain is working on http:// (without SSL) then we can configure to use SSL. We want http://shawlw.me, http://www.shawlw.me, http://aws.shawlw.me, http://www.aws.shawlw.me to be working.

We will then install free certificates from Let’s Encrypt. See my other articles on installing certbot, but again, take note of the comments below, as certbot is messy. If you make mistakes you have to clean them up. The configurations will show what you need to end up having for ssl to work on the subdomain as well.

We will not install a multi-domain certificate from a 3rd party like Commodo or Sertigo. However, if you know how to do those, that would be fine. The entries on the primary and subdomain .conf files would list the .crt and .key files for /etc/pki/tls/certs dn /etc/pki/tls/private, where the .crt file is correctly chained and includes two entries for the domain and subdomain, or possibly four to include www. Note, multi-certs cost a lot of money for us regular folks, so you would have to pay for yourdoman.com and subdomain.yourdomain.com. My concern is how these may not handle www with both and could cost money to work out how to do it, as once you purchase a certificate yu cannot modify it. I have already had failures on testing with openssl configurations and had a client delay an installation, so I can’t offer advice as yet on the multi-certificates.

Once this is working, as per your own testing, (I give a testing example) you then install WordPress twice – to https://yourdomain.com (or whatever) and https://subdomain.yourdomain.com (or whatever).

We will fix httpd.conf to accept the subdomain.

We will already have worked with various domain and subdomain /etc/httpd/conf.d  .conf files.

You can use the same or different WP themes.

I had config issues with the plugin Wordfence, so I include examples of the configurations and the .htaccess files that worked for myself. I have PHP v8.1, so I added an entry into the .htaccess Wordfence section for PHP8.

Once WP is installed, you can check the database entries and will verify the site url and home url in the tables are indeed given as the subdomain https://subdomain.yourdomain.com, rather than https://yourdomain.com/subdomain.

Why use a subdomain?

I don’t see value unless there is a major work that needs good public exposure and better search engine ranking.

You may notice I do not use a subdomain for my own two websites of photographybyshaw.au and photographybyshaw.au/aws. I also do not use a subdomain for my S3 buckets that show my galleries.

One should also make a decision as to whether the subdomain is on the same instance or a new instance. If there is a security breach, it impacts the primary domain instance or vice versa. If you use an additional instance, it costs more, but performance may be better.

I personally would not want to see heavier internet traffic from the two domains on the one instance. If traffic is light, one instance should be fine. Always keep in mind that Amazon instance credit specifications should be standard so that if CPU is over 100% you do not get bill shock. I have never seen my sites above low levels of CPU. A higher volume site can always go onto a higher paying instance for more CPU and RAM.

Assumptions below are that you have already become familiar with installing instances, are familiar with Route 53, and have played enough with certbot to tackle it’s potential or probable tangles.


Here are the screenshots for Route 53 using my shawlw.me example.


This is all you need for the two hosted zone basic setups.

You can monitor DNS propagation at:


Entries must propagate before you try your http:// browser tests below, otherwise it is hit and miss as to whether the domain and subdomain are found.

Configure http:// (without SSL)

Install an instance, only using the basic http packages.

See my installation notes on Linux 2022. You would configure the disk swap space, set the apache user and permissions, and just do a simple install of http with:

dnf install -y httpd httpd-tools


The instance would have the IP address you assign in Route 53 as shown above.

You would create the subfolder, e.g.


You can put test directories in ./html and ./html/aws with some photos or images under them to test.

e.g., use Filezilla to upload a.jpg and b.jpg, and have testing under anything, like,



Make sure ./html is owned by apache, ./aws is chmod 2775, and all files are chown and chgrp apache and chmod 664.

In the ./html directory add anything into an index.html file, e.g.

hello world – this is /var/www/html

In the ./html/aws directory add an index.html file, e.g.

hello world – this is subdomain /var/www/html/aws

Let us assume you have configured /etc/httpd/conf/httpd.conf as usual, and /etc/httpd/conf.modules.d as per my install notes so that httpd is working at a basic level.

Add a file, e.g. asw.conf to /etc/httpd/conf.d with the following lines:

<VirtualHost *:80>
    ServerName aws.shawlw.me
    ServerAlias www.aws.shawlw.me
    DocumentRoot /var/www/html/aws

When httpd restarts it will include this .conf file.

Issue the “systemctl restart httpd” command.

If errors, check out the /etc/httpd/logs or use the journal command given on the error message to check things.

All the above should work, so you can now open a browser and type:



You can then test the images:



We will add entries for www in the ssl configuration files in a moment.

Configure https:// (with SSL)

Let us assume you have installed pip for certbot, and the certbot packages, as per my notes if you need those.

Previously we only installed httpd. We now install ssl:

dnf install -y mod_ssl 

We can continue with some other basics as per my installation notes just to get php8.1 up and running. e.g.:

dnf install -y php php-common php-pear wget php-mysqli php-devel php-mbstring
dnf install -y php-cli php-pdo php-fpm php-json php-mysqlnd php-opcache
dnf install -y gd libzip-devel httpd-devel kernel-devel php-gd postfix


We do not want to restart httpd just yet.

As per my installation notes, configure /etc/httpd/conf.d/ssl.conf.

This will have the ServerName added, the SSL certificate entries and so forth, but nothing about certbot at this stage.

We must have the VirtualHost entry at the top:

<VirtualHost *:80>
ServerName shawlw.me
Redirect permanent / https://shawlw.me/
RewriteEngine on
RewriteCond %{SERVER_NAME} =shawlw.me
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

[And ensure you have included these entries with your own domain name of course, under the :443 VH section...]

<VirtualHost _default_:443>
DocumentRoot "/var/www/html"
ServerName shawlw.me:443

[And you will have the various SSL certificate entries as per my install notes, such as SSLProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2 and so forth.]


At this stage there are no certbot entries.

We already have /etc/httpd/conf.d/aws.conf.

Let’s add an SSL file for the subdomain. e.g.:


The contents will be almost the same as ssl.conf but will not have any of the lines from ssl.conf above the :443 VH line.

[At the top of the file...]

<VirtualHost *:80>
ServerName aws.shawlw.me
Redirect permanent / https://aws.shawlw.me/
RewriteEngine on
RewriteCond %{SERVER_NAME} =aws.shawlw.me
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

[Then change the _default_:443 line to read, and add to it these lines (with your own subdomain name):]

<VirtualHost *:443>

DocumentRoot "/var/www/html/aws"

ServerName aws.shawlw.me:443

[You will already have things like SSLProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2 added to the file, from above steps.
Then replace the existing log files with some filenames, e.g.: (no need to touch httpd.conf)]

ErrorLog logs/aws.ssl_error_log
TransferLog logs/aws.ssl_access_log
CustomLog logs/aws.ssl_request_log \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

At this stage httpd is running without SSL.

We do not restart httpd yet as it will fail when trying to load ssl.

We now run certbot…

/usr/bin/certbot -v certonly -d shawlw.me -d www.shawlw.me --webroot -w /var/www/html --dry-run
/usr/bin/certbot -v certonly -d aws.shawlw.me -d www.aws.shawlw.me --webroot -w /var/www/html/aws --dry-run

[If these do no work, look closely at the errors.
Once working, execute without the dry run - using your own domain names.
Now we run:]

/usr/bin/certbot --apache

[This is where we get a bit of a mess. We manually edit ssl.conf and aws.ssl.conf to have these at the bottom of the files just before the closing </VirtualHost> line: (with your own domain names)

ServerAlias shawlw.me
ServerAlias www.shawlw.me
SSLCertificateFile /etc/letsencrypt/live/shawlw.me/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/shawlw.me/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf

[and for aws.ssl.conf:]

ServerAlias aws.shawlw.me
ServerAlias www.aws.shawlw.me
SSLCertificateFile /etc/letsencrypt/live/aws.shawlw.me/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/aws.shawlw.me/privkey.pem

[We do not add the Include line.]

When you do the above, there is likely going to be another file in ./conf.d that certbot creates.

After running certbot –apache you will get an additional certificate made called yourdomain-0001, and you have to manually edit the ssl files to put it back to the original domain names without 0001, and go to /etc/letsencrypt and manually delete all 0001 entries. A mess, but it works.

If you are wanting to go back on your steps are get back to http:// without SSL, you can cp -p your ssl files to ssl.conf.o and aws.ssl.conf.o but there is likely a certbot file you need to move, such as:

cd /etc/httpd/conf.d
mv le-redirect-shawlw.me:443.conf le-redirect-shawlw.me:443.conf.o

Now restart httpd, and you should have SSL working, and ability to test:





and test the image files are ok.

You can also review the error logs are working separately for domain and subdomain.

You can then test the certificates at ssllabs if you wish.

We are ready to install WordPress.

Install WordPress

We will do a vanilla install of WordPress to /var/www/html and /var/www/hmtl/aws.

Finish installing the instance packages, because you know SSL is working.
(See my Linux 2022 install notes)

Use FileZilla to upload the WordPress .zip file.


[Assuming you unzipped WordPress into /home/ec2-user...]

cd /home/ec2-user
cd wordpress
cp -pR * /var/www/hmtl
cp -pR * /var/www/html/aws

Fix the Apache permissions – as per my install notes, for /var/www/html:


[I use a shell script called chdir.sh with permission of 777:]

cd /var/www/html
vi chdir.sh

chown -R apache *
chgrp -R apache *
find . -type d -exec chmod 2775 {} \;
find . -type f -exec chmod 0664 {} \;
if [ -f "./.htaccess" ] ; then
chown apache .htaccess
chgrp apache .htaccess
chmod 664 .htaccess
chmod 777 *.sh
chown root chdir.sh
chgrp root chdir.sh
chmod 770 chdir.sh

[Save and exit. Now execute the script:]

chmod 777 chdir.sh

ls -la

Now create two databases with users, one for yourdomain, and one for your subdomain.

Then run the WordPress installations as usual:



Then verify the entries in phpMyAdmin show https://aws.shawlw.me in the subdomain’s database options table. (Use your own domain name of course)

Lastly, fix /etc/httpd/conf/httpd.conf by adding below the /var/www/html section:

vi /etc/httpd/conf/httpd.conf

[Search for the section <Directory "/var/www/html"> and after the closing </Directory> add with your own subdomain name:]

<Directory "/var/www/html/aws">
 Options Indexes FollowSymLinks
AllowOverride All
 Require all granted

At some point before installing WordPress you should have the instance fully configured, and do a sync;sync;reboot of the instance. If WordPress is having issues, make sure you do the reboot, or restart the entire instance from the EC2 console, and remember that your browser may have old caching that interferes.

The above steps do work, but need your problem solving skills.

Post Installation Notes

Post Installation Notes

I think it is perhaps easier to use certbot –apache and enter the domain name and www.domain name, ensuring ssl.conf is present, rather than the initial certbot example I have given.

The configs for wordfence that are working:

[for the main domain /var/www/html/.htaccess file: (I assume I used the same for /var/www/html/aws/.htaccess but I forgot to check.]

# Wordfence WAF
<Files ".user.ini">
<IfModule mod_authz_core.c>
    Require all denied
<IfModule !mod_authz_core.c>
    Order deny,allow
    Deny from all

<IfModule mod_php5.c>
    php_value auto_prepend_file '/var/www/html/wordfence-waf.php'
<IfModule mod_php7.c>
    php_value auto_prepend_file '/var/www/html/wordfence-waf.php'
<IfModule mod_php.c>
    php_value auto_prepend_file '/var/www/html/wordfence-waf.php'
<IfModule mod_php8.c>
        php_value auto_prepend_file '/var/www/html/wordfence-waf.php'
<Files ".user.ini">
<IfModule mod_authz_core.c>
    Require all denied
<IfModule !mod_authz_core.c>
    Order deny,allow
    Deny from all

# END Wordfence WAF

[Entries at the end of /var/www/html/aws/.htaccess:]

# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
# Any changes to the directives between these markers will be overwritten.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /aws/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /aws/index.php [L]

# END WordPress


; Wordfence WAF
auto_prepend_file = '/var/www/html/wordfence-waf.php'
; END Wordfence WAF

[/var/www/hmtl/wordfence-waf.php and /var/www/html/aws/wordfence-wag.php:]
// Before removing this file, please verify the PHP ini setting `auto_prepend_file` does not point to this.

if (file_exists(__DIR__.'/wp-content/plugins/wordfence/waf/bootstrap.php')) {
    define("WFWAF_LOG_PATH", __DIR__.'/wp-content/wflogs/');
    include_once __DIR__.'/wp-content/plugins/wordfence/waf/bootstrap.php';

; Wordfence WAF
auto_prepend_file = '/var/www/html/aws/wordfence-waf.php'
; END Wordfence WAF

Note that It may take a couple of goes to enable the Wordfence firewall in Linux 2022.

Start typing and press Enter to search