

# AL2 Tutorials
<a name="al2-tutorials"></a>

 The following tutorials show you how to perform common tasks using Amazon EC2 instances running AL2. For video tutorials, see [AWS Instructional videos and labs](https://www.aws.training/). 

For AL2023 instructions, see [Tutorials](https://docs.aws.amazon.com/linux/al2023/ug/tutorials-al2023.html) in the *Amazon Linux 2023 User Guide*.

**Topics**
+ [Tutorial: Install a LAMP server on AL2](ec2-lamp-amazon-linux-2.md)
+ [Tutorial: Configure SSL/TLS on AL2](SSL-on-amazon-linux-2.md)
+ [Tutorial: Host a WordPress blog on AL2](hosting-wordpress.md)

# Tutorial: Install a LAMP server on AL2
<a name="ec2-lamp-amazon-linux-2"></a>

The following procedures help you install an Apache web server with PHP and [MariaDB](https://mariadb.org/about/) (a community-developed fork of MySQL) support on your AL2 instance (sometimes called a LAMP web server or LAMP stack). You can use this server to host a static website or deploy a dynamic PHP application that reads and writes information to a database.

**Important**  
If you are trying to set up a LAMP web server on a different distribution, such as Ubuntu or Red Hat Enterprise Linux, this tutorial will not work. For AL2023, see [Install a LAMP server on AL2023](https://docs.aws.amazon.com//linux/al2023/ug/ec2-lamp-amazon-linux-2023.html). For Ubuntu, see the following Ubuntu community documentation: [ApacheMySQLPHP](https://help.ubuntu.com/community/ApacheMySQLPHP). For other distributions, see their specific documentation.

**Option: Complete this tutorial using automation**  
To complete this tutorial using AWS Systems Manager Automation instead of the following tasks, run the [AWSDocs-InstallALAMPServer-AL2](https://console.aws.amazon.com/systems-manager/automation/execute/AWSDocs-InstallALAMPServer-AL2) Automation document.

**Topics**
+ [Step 1: Prepare the LAMP server](#prepare-lamp-server)
+ [Step 2: Test your LAMP server](#test-lamp-server)
+ [Step 3: Secure the database server](#secure-mariadb-lamp-server)
+ [Step 4: (Optional) Install phpMyAdmin](#install-phpmyadmin-lamp-server)
+ [Troubleshoot](#lamp-troubleshooting)
+ [Related topics](#lamp-more-info)

## Step 1: Prepare the LAMP server
<a name="prepare-lamp-server"></a>

**Prerequisites**
+ This tutorial assumes that you have already launched a new instance using AL2, with a public DNS name that is reachable from the internet. For more information, see [Launch an instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-instance-wizard.html) in the *Amazon EC2 User Guide*. You must also have configured your security group to allow SSH (port 22), HTTP (port 80), and HTTPS (port 443) connections. For more information about these prerequisites, see [Security group rules](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-rules.html) in the *Amazon EC2 User Guide*.
+ The following procedure installs the latest PHP version available on AL2, currently `php8.2`. If you plan to use PHP applications other than those described in this tutorial, you should check their compatibility with `php8.2`.<a name="install_apache-2"></a>

**To prepare the LAMP server**

1. [Connect](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html) to your instance.

1. To ensure that all of your software packages are up to date, perform a quick software update on your instance. This process may take a few minutes, but it is important to make sure that you have the latest security updates and bug fixes.

   The `-y` option installs the updates without asking for confirmation. If you would like to examine the updates before installing, you can omit this option.

   ```
   [ec2-user ~]$ sudo yum update -y
   ```

1. Install the `mariadb10.5` Amazon Linux Extras repositories to get the latest version of the MariaDB package.

   ```
   [ec2-user ~]$ sudo amazon-linux-extras install mariadb10.5
   ```

   If you receive an error stating `sudo: amazon-linux-extras: command not found`, then your instance was not launched with an Amazon Linux 2 AMI (perhaps you are using the Amazon Linux AMI instead). You can view your version of Amazon Linux using the following command.

   ```
   cat /etc/system-release
   ```

1. Install the `php8.2` Amazon Linux Extras repositories to get the latest version of the PHP package for AL2.

   ```
   [ec2-user ~]$ sudo amazon-linux-extras install php8.2
   ```

1. Now that your instance is current, you can install the Apache web server, MariaDB, and PHP software packages. Use the yum install command to install multiple software packages and all related dependencies at the same time

   ```
   [ec2-user ~]$ sudo yum install -y httpd
   ```

   You can view the current versions of these packages using the following command:

   ```
   yum info package_name
   ```

1. Start the Apache web server.

   ```
   [ec2-user ~]$ sudo systemctl start httpd
   ```

1.  Use the **systemctl** command to configure the Apache web server to start at each system boot. 

   ```
   [ec2-user ~]$ sudo systemctl enable httpd
   ```

   You can verify that **httpd** is on by running the following command:

   ```
   [ec2-user ~]$ sudo systemctl is-enabled httpd
   ```

1. Add a security rule to allow inbound HTTP (port 80) connections to your instance if you have not already done so. By default, a **launch-wizard-*N*** security group was set up for your instance during initialization. This group contains a single rule to allow SSH connections. 

   1. Open the Amazon EC2 console at [https://console.aws.amazon.com/ec2/](https://console.aws.amazon.com/ec2/).

   1. Choose **Instances** and select your instance.

   1. On the **Security** tab, view the inbound rules. You should see the following rule:

      ```
      Port range   Protocol     Source
      22           tcp          0.0.0.0/0
      ```
**Warning**  
Using `0.0.0.0/0` allows all IPv4 addresses to access your instance using SSH. This is acceptable for a short time in a test environment, but it's unsafe for production environments. In production, you authorize only a specific IP address or range of addresses to access your instance.

   1. Choose the link for the security group. Using the procedures in [Add rules to a security group](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-security-groups.html#adding-security-group-rule), add a new inbound security rule with the following values:
      + **Type**: HTTP
      + **Protocol**: TCP
      + **Port Range**: 80
      + **Source**: Custom

1. Test your web server. In a web browser, type the public DNS address (or the public IP address) of your instance. If there is no content in `/var/www/html`, you should see the Apache test page. You can get the public DNS for your instance using the Amazon EC2 console (check the **Public DNS** column; if this column is hidden, choose **Show/Hide Columns** (the gear-shaped icon) and choose **Public DNS**).

   Verify that the security group for the instance contains a rule to allow HTTP traffic on port 80. For more information, see [Add rules to security group](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-security-groups.html#adding-security-group-rule).
**Important**  
If you are not using Amazon Linux, you may also need to configure the firewall on your instance to allow these connections. For more information about how to configure the firewall, see the documentation for your specific distribution.  
![\[The test of the server shows the Apache test page.\]](http://docs.aws.amazon.com/linux/al2/ug/images/apache_test_page_al2_2.4.png)

Apache **httpd** serves files that are kept in a directory called the Apache document root. The Amazon Linux Apache document root is `/var/www/html`, which by default is owned by root.

To allow the `ec2-user` account to manipulate files in this directory, you must modify the ownership and permissions of the directory. There are many ways to accomplish this task. In this tutorial, you add `ec2-user` to the `apache` group, to give the `apache` group ownership of the `/var/www` directory and assign write permissions to the group.<a name="setting-file-permissions-2"></a>

**To set file permissions**

1. Add your user (in this case, `ec2-user`) to the `apache` group.

   ```
   [ec2-user ~]$ sudo usermod -a -G apache ec2-user
   ```

1. Log out and then log back in again to pick up the new group, and then verify your membership.

   1. Log out (use the **exit** command or close the terminal window):

      ```
      [ec2-user ~]$ exit
      ```

   1. To verify your membership in the `apache` group, reconnect to your instance, and then run the following command:

      ```
      [ec2-user ~]$ groups
      ec2-user adm wheel apache systemd-journal
      ```

1. Change the group ownership of `/var/www` and its contents to the `apache` group.

   ```
   [ec2-user ~]$ sudo chown -R ec2-user:apache /var/www
   ```

1. To add group write permissions and to set the group ID on future subdirectories, change the directory permissions of `/var/www` and its subdirectories.

   ```
   [ec2-user ~]$ sudo chmod 2775 /var/www && find /var/www -type d -exec sudo chmod 2775 {} \;
   ```

1. To add group write permissions, recursively change the file permissions of `/var/www` and its subdirectories:

   ```
   [ec2-user ~]$ find /var/www -type f -exec sudo chmod 0664 {} \;
   ```

Now, `ec2-user` (and any future members of the `apache` group) can add, delete, and edit files in the Apache document root, enabling you to add content, such as a static website or a PHP application.

**To secure your web server (Optional)**  
A web server running the HTTP protocol provides no transport security for the data that it sends or receives. When you connect to an HTTP server using a web browser, the URLs that you visit, the content of webpages that you receive, and the contents (including passwords) of any HTML forms that you submit are all visible to eavesdroppers anywhere along the network pathway. The best practice for securing your web server is to install support for HTTPS (HTTP Secure), which protects your data with SSL/TLS encryption.

For information about enabling HTTPS on your server, see [Tutorial: Configure SSL/TLS on AL2](SSL-on-amazon-linux-2.md).

## Step 2: Test your LAMP server
<a name="test-lamp-server"></a>

If your server is installed and running, and your file permissions are set correctly, your `ec2-user` account should be able to create a PHP file in the `/var/www/html` directory that is available from the internet.

**To test your LAMP server**

1. Create a PHP file in the Apache document root.

   ```
   [ec2-user ~]$ echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php
   ```

   If you get a "Permission denied" error when trying to run this command, try logging out and logging back in again to pick up the proper group permissions that you configured in [To set file permissions](#setting-file-permissions-2).

1. In a web browser, type the URL of the file that you just created. This URL is the public DNS address of your instance followed by a forward slash and the file name. For example:

   ```
   http://my.public.dns.amazonaws.com/phpinfo.php
   ```

   You should see the PHP information page:  
![\[Test of the LAMP server shows the PHP information page.\]](http://docs.aws.amazon.com/linux/al2/ug/images/phpinfo7.2.10.png)

   If you do not see this page, verify that the `/var/www/html/phpinfo.php` file was created properly in the previous step. You can also verify that all of the required packages were installed with the following command.

   ```
   [ec2-user ~]$ sudo yum list installed httpd mariadb-server php-mysqlnd
   ```

   If any of the required packages are not listed in your output, install them with the **sudo yum install *package*** command. Also verify that the `php7.2` and `lamp-mariadb10.2-php7.2` extras are enabled in the output of the **amazon-linux-extras** command.

1. Delete the `phpinfo.php` file. Although this can be useful information, it should not be broadcast to the internet for security reasons.

   ```
   [ec2-user ~]$ rm /var/www/html/phpinfo.php
   ```

You should now have a fully functional LAMP web server. If you add content to the Apache document root at `/var/www/html`, you should be able to view that content at the public DNS address for your instance. 

## Step 3: Secure the database server
<a name="secure-mariadb-lamp-server"></a>

The default installation of the MariaDB server has several features that are great for testing and development, but they should be disabled or removed for production servers. The **mysql\$1secure\$1installation** command walks you through the process of setting a root password and removing the insecure features from your installation. Even if you are not planning on using the MariaDB server, we recommend performing this procedure.<a name="securing-maria-db"></a>

**To secure the MariaDB server**

1. Start the MariaDB server.

   ```
   [ec2-user ~]$ sudo systemctl start mariadb
   ```

1. Run **mysql\$1secure\$1installation**.

   ```
   [ec2-user ~]$ sudo mysql_secure_installation
   ```

   1. When prompted, type a password for the root account.

      1. Type the current root password. By default, the root account does not have a password set. Press Enter.

      1. Type **Y** to set a password, and type a secure password twice. For more information about creating a secure password, see [https://identitysafe.norton.com/password-generator/](https://identitysafe.norton.com/password-generator/). Make sure to store this password in a safe place.

         Setting a root password for MariaDB is only the most basic measure for securing your database. When you build or install a database-driven application, you typically create a database service user for that application and avoid using the root account for anything but database administration. 

   1. Type **Y** to remove the anonymous user accounts.

   1. Type **Y** to disable the remote root login.

   1. Type **Y** to remove the test database.

   1. Type **Y** to reload the privilege tables and save your changes.

1. (Optional) If you do not plan to use the MariaDB server right away, stop it. You can restart it when you need it again.

   ```
   [ec2-user ~]$ sudo systemctl stop mariadb
   ```

1. (Optional) If you want the MariaDB server to start at every boot, type the following command.

   ```
   [ec2-user ~]$ sudo systemctl enable mariadb
   ```

## Step 4: (Optional) Install phpMyAdmin
<a name="install-phpmyadmin-lamp-server"></a>

[phpMyAdmin](https://www.phpmyadmin.net/) is a web-based database management tool that you can use to view and edit the MySQL databases on your EC2 instance. Follow the steps below to install and configure `phpMyAdmin` on your Amazon Linux instance.

**Important**  
We do not recommend using `phpMyAdmin` to access a LAMP server unless you have enabled SSL/TLS in Apache; otherwise, your database administrator password and other data are transmitted insecurely across the internet. For security recommendations from the developers, see [Securing your phpMyAdmin installation](https://docs.phpmyadmin.net/en/latest/setup.html#securing-your-phpmyadmin-installation). For general information about securing a web server on an EC2 instance, see [Tutorial: Configure SSL/TLS on AL2](SSL-on-amazon-linux-2.md).

**To install phpMyAdmin**

1. Install the required dependencies.

   ```
   [ec2-user ~]$ sudo yum install php-mbstring php-xml -y
   ```

1. Restart Apache.

   ```
   [ec2-user ~]$ sudo systemctl restart httpd
   ```

1. Restart `php-fpm`.

   ```
   [ec2-user ~]$ sudo systemctl restart php-fpm
   ```

1. Navigate to the Apache document root at `/var/www/html`.

   ```
   [ec2-user ~]$ cd /var/www/html
   ```

1. Select a source package for the latest phpMyAdmin release from [https://www.phpmyadmin.net/downloads](https://www.phpmyadmin.net/downloads). To download the file directly to your instance, copy the link and paste it into a **wget** command, as in this example:

   ```
   [ec2-user html]$ wget https://www.phpmyadmin.net/downloads/phpMyAdmin-latest-all-languages.tar.gz
   ```

1. Create a `phpMyAdmin` folder and extract the package into it with the following command.

   ```
   [ec2-user html]$ mkdir phpMyAdmin && tar -xvzf phpMyAdmin-latest-all-languages.tar.gz -C phpMyAdmin --strip-components 1
   ```

1. Delete the *phpMyAdmin-latest-all-languages.tar.gz* tarball.

   ```
   [ec2-user html]$ rm phpMyAdmin-latest-all-languages.tar.gz
   ```

1.  (Optional) If the MySQL server is not running, start it now.

   ```
   [ec2-user ~]$ sudo systemctl start mariadb
   ```

1. In a web browser, type the URL of your phpMyAdmin installation. This URL is the public DNS address (or the public IP address) of your instance followed by a forward slash and the name of your installation directory. For example:

   ```
   http://my.public.dns.amazonaws.com/phpMyAdmin
   ```

   You should see the phpMyAdmin login page:  
![\[Result of typing the URL of your phpMyAdmin installation is the phpMyAdmin login screen.\]](http://docs.aws.amazon.com/linux/al2/ug/images/phpmyadmin_login.png)

1. Log in to your phpMyAdmin installation with the `root` user name and the MySQL root password you created earlier.

   Your installation must still be configured before you put it into service. We suggest that you begin by manually creating the configuration file, as follows:

   1. To start with a minimal configuration file, use your favorite text editor to create a new file, and then copy the contents of `config.sample.inc.php` into it.

   1. Save the file as `config.inc.php` in the phpMyAdmin directory that contains `index.php`.

   1. Refer to post-file creation instructions in the [Using the Setup script](https://docs.phpmyadmin.net/en/latest/setup.html#using-the-setup-script) section of the phpMyAdmin installation instructions for any additional setup.

    For information about using phpMyAdmin, see the [phpMyAdmin User Guide](http://docs.phpmyadmin.net/en/latest/user.html).

## Troubleshoot
<a name="lamp-troubleshooting"></a>

This section offers suggestions for resolving common problems you may encounter while setting up a new LAMP server. 

### I can't connect to my server using a web browser
<a name="is_apache_on"></a>

Perform the following checks to see if your Apache web server is running and accessible.
+ **Is the web server running?**

  You can verify that **httpd** is on by running the following command:

  ```
  [ec2-user ~]$ sudo systemctl is-enabled httpd
  ```

  If the **httpd** process is not running, repeat the steps described in [To prepare the LAMP server](#install_apache-2).
+ **Is the firewall correctly configured?**

  Verify that the security group for the instance contains a rule to allow HTTP traffic on port 80. For more information, see [Add rules to security group](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-security-groups.html#adding-security-group-rule).

### I can't connect to my server using HTTPS
<a name="is-https-enabled"></a>

Perform the following checks to see if your Apache web server is configured to support HTTPS.
+ **Is the web server correctly configured?**

  After you install Apache, the server is configured for HTTP traffic. To support HTTPS, enable TLS on the server and install an SSL certificate. For information, see [Tutorial: Configure SSL/TLS on AL2](SSL-on-amazon-linux-2.md).
+ **Is the firewall correctly configured?**

  Verify that the security group for the instance contains a rule to allow HTTPS traffic on port 443. For more information, see [Add rules to a security group](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-security-groups.html#adding-security-group-rule).

## Related topics
<a name="lamp-more-info"></a>

For more information about transferring files to your instance or installing a WordPress blog on your web server, see the following documentation:
+ [Transfer files to your Linux instance using WinSCP](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/putty.html#Transfer_WinSCP).
+ [Transfer files to Linux instances using an SCP client](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-linux-inst-ssh.html#linux-file-transfer-scp).
+ [Tutorial: Host a WordPress blog on AL2](hosting-wordpress.md)

For more information about the commands and software used in this tutorial, see the following webpages:
+ Apache web server: [http://httpd.apache.org/](http://httpd.apache.org/)
+ MariaDB database server: [https://mariadb.org/](https://mariadb.org/)
+ PHP programming language: [http://php.net/](http://php.net/)
+ The `chmod` command: [https://en.wikipedia.org/wiki/Chmod](https://en.wikipedia.org/wiki/Chmod)
+ The `chown` command: [https://en.wikipedia.org/wiki/Chown](https://en.wikipedia.org/wiki/Chown)

For more information about registering a domain name for your web server, or transferring an existing domain name to this host, see [Creating and Migrating Domains and Subdomains to Amazon Route 53](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/creating-migrating.html) in the *Amazon Route 53 Developer Guide*.

# Tutorial: Configure SSL/TLS on AL2
<a name="SSL-on-amazon-linux-2"></a>

Secure Sockets Layer/Transport Layer Security (SSL/TLS) creates an encrypted channel between a web server and web client that protects data in transit from being eavesdropped on. This tutorial explains how to add support manually for SSL/TLS on an EC2 instance with AL2 and Apache web server. This tutorial assumes that you are not using a load balancer. If you are using Elastic Load Balancing, you can choose to configure SSL offload on the load balancer, using a certificate from [AWS Certificate Manager](https://aws.amazon.com/certificate-manager/) instead.

For historical reasons, web encryption is often referred to simply as SSL. While web browsers still support SSL, its successor protocol TLS is less vulnerable to attack. AL2 disables server-side support for all versions of SSL by default. [Security standards bodies](https://www.ssl.com/article/deprecating-early-tls/) consider TLS 1.0 to be unsafe. TLS 1.0 and TLS 1.1 were formally [deprecated](https://datatracker.ietf.org/doc/rfc8996/) in March 2021. This tutorial contains guidance based exclusively on enabling TLS 1.2. TLS 1.3 was finalized in 2018 and is available in AL2 as long as the underlying TLS library (OpenSSL in this tutorial) is supported and enabled. [Clients must support TLS 1.2 or later by June 28, 2023](https://aws.amazon.com/blogs/security/tls-1-2-required-for-aws-endpoints/). For more information about the updated encryption standards, see [RFC 7568](https://tools.ietf.org/html/rfc7568) and [RFC 8446](https://tools.ietf.org/html/rfc8446).

This tutorial refers to modern web encryption simply as TLS.

**Important**  
These procedures are intended for use with AL2. We also assume that you are starting with a new Amazon EC2 instance. If you are trying to set up an EC2 instance running a different distribution, or an instance running an old version of AL2, some procedures in this tutorial might not work. For Ubuntu, see the following community documentation: [Open SSL on Ubuntu](https://help.ubuntu.com/community/OpenSSL). For Red Hat Enterprise Linux, see the following: [Setting up the Apache HTTP Web Server](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/deploying_different_types_of_servers/setting-apache-http-server_deploying-different-types-of-servers). For other distributions, see their specific documentation.

**Note**  
Alternatively, you can use AWS Certificate Manager (ACM) for AWS Nitro enclaves, which is an enclave application that allows you to use public and private SSL/TLS certificates with your web applications and servers running on Amazon EC2 instances with AWS Nitro Enclaves. Nitro Enclaves is an Amazon EC2 capability that enables creation of isolated compute environments to protect and securely process highly sensitive data, such as SSL/TLS certificates and private keys.  
ACM for Nitro Enclaves works with **nginx** running on your Amazon EC2 Linux instance to create private keys, to distribute certificates and private keys, and to manage certificate renewals.  
To use ACM for Nitro Enclaves, you must use an enclave-enabled Linux instance.  
For more information, see [ What is AWS Nitro Enclaves?](https://docs.aws.amazon.com/enclaves/latest/user/nitro-enclave.html) and [AWS Certificate Manager for Nitro Enclaves](https://docs.aws.amazon.com/enclaves/latest/user/nitro-enclave-refapp.html) in the *AWS Nitro Enclaves User Guide*.

**Topics**
+ [Prerequisites](#ssl_prereq)
+ [Step 1: Enable TLS on the server](#ssl_enable)
+ [Step 2: Obtain a CA-signed certificate](#ssl_certificate)
+ [Step 3: Test and harden the security configuration](#ssl_test)
+ [Troubleshoot](#troubleshooting)

## Prerequisites
<a name="ssl_prereq"></a>

Before you begin this tutorial, complete the following steps:
+ Launch an Amazon EBS backed AL2 instance. For more information, see [Launch an instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-instance-wizard.html) in the *Amazon EC2 User Guide*.
+ Configure your security groups to allow your instance to accept connections on the following TCP ports: 
  + SSH (port 22)
  + HTTP (port 80)
  + HTTPS (port 443)

  For more information, see [Security group rules](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-rules.html) in the *Amazon EC2 User Guide*.
+ Install the Apache web server. For step-by-step instructions, see [Tutorial: Install a LAMP Web Server on AL2](ec2-lamp-amazon-linux-2.md). Only the httpd package and its dependencies are needed, so you can ignore the instructions involving PHP and MariaDB.
+ To identify and authenticate websites, the TLS public key infrastructure (PKI) relies on the Domain Name System (DNS). To use your EC2 instance to host a public website, you need to register a domain name for your web server or transfer an existing domain name to your Amazon EC2 host. Numerous third-party domain registration and DNS hosting services are available for this, or you can use [Amazon Route 53](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/Welcome.html). 

## Step 1: Enable TLS on the server
<a name="ssl_enable"></a>

**Option: Complete this tutorial using automation**  
To complete this tutorial using AWS Systems Manager Automation instead of the following tasks, run the [automation document](https://console.aws.amazon.com/systems-manager/documents/AWSDocs-Configure-SSL-TLS-AL2/).

This procedure takes you through the process of setting up TLS on AL2 with a self-signed digital certificate. 

**Note**  
A self-signed certificate is acceptable for testing but not production. If you expose your self-signed certificate to the internet, visitors to your site are greeted by security warnings. 

**To enable TLS on a server**

1. [Connect](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html) to your instance and confirm that Apache is running.

   ```
   [ec2-user ~]$ sudo systemctl is-enabled httpd
   ```

   If the returned value is not "enabled," start Apache and set it to start each time the system boots.

   ```
   [ec2-user ~]$ sudo systemctl start httpd && sudo systemctl enable httpd
   ```

1. To ensure that all of your software packages are up to date, perform a quick software update on your instance. This process may take a few minutes, but it is important to make sure that you have the latest security updates and bug fixes.
**Note**  
The `-y` option installs the updates without asking for confirmation. If you would like to examine the updates before installing, you can omit this option.

   ```
   [ec2-user ~]$ sudo yum update -y
   ```

1. Now that your instance is current, add TLS support by installing the Apache module `mod_ssl`.

   ```
   [ec2-user ~]$ sudo yum install -y mod_ssl
   ```

   Your instance now has the following files that you use to configure your secure server and create a certificate for testing:
   +  `/etc/httpd/conf.d/ssl.conf` 

     The configuration file for mod\$1ssl. It contains *directives* telling Apache where to find encryption keys and certificates, the TLS protocol versions to allow, and the encryption ciphers to accept. 
   + `/etc/pki/tls/certs/make-dummy-cert`

     A script to generate a self-signed X.509 certificate and private key for your server host. This certificate is useful for testing that Apache is properly set up to use TLS. Because it offers no proof of identity, it should not be used in production. If used in production, it triggers warnings in Web browsers.

1. Run the script to generate a self-signed dummy certificate and key for testing.

   ```
   [ec2-user ~]$ cd /etc/pki/tls/certs
   sudo ./make-dummy-cert localhost.crt
   ```

   This generates a new file `localhost.crt` in the `/etc/pki/tls/certs/` directory. The specified file name matches the default that is assigned in the **SSLCertificateFile** directive in `/etc/httpd/conf.d/ssl.conf`. 

   This file contains both a self-signed certificate and the certificate's private key. Apache requires the certificate and key to be in PEM format, which consists of Base64-encoded ASCII characters framed by "BEGIN" and "END" lines, as in the following abbreviated example.

   ```
   -----BEGIN PRIVATE KEY-----
   MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD2KKx/8Zk94m1q
   3gQMZF9ZN66Ls19+3tHAgQ5Fpo9KJDhzLjOOCI8u1PTcGmAah5kEitCEc0wzmNeo
   BCl0wYR6G0rGaKtK9Dn7CuIjvubtUysVyQoMVPQ97ldeakHWeRMiEJFXg6kZZ0vr
   GvwnKoMh3DlK44D9dX7IDua2PlYx5+eroA+1Lqf32ZSaAO0bBIMIYTHigwbHMZoT
   ...
   56tE7THvH7vOEf4/iUOsIrEzaMaJ0mqkmY1A70qQGQKBgBF3H1qNRNHuyMcPODFs
   27hDzPDinrquSEvoZIggkDMlh2irTiipJ/GhkvTpoQlv0fK/VXw8vSgeaBuhwJvS
   LXU9HvYq0U6O4FgD3nAyB9hI0BE13r1HjUvbjT7moH+RhnNz6eqqdscCS09VtRAo
   4QQvAqOa8UheYeoXLdWcHaLP
   -----END PRIVATE KEY-----                    
   
   -----BEGIN CERTIFICATE-----
   MIIEazCCA1OgAwIBAgICWxQwDQYJKoZIhvcNAQELBQAwgbExCzAJBgNVBAYTAi0t
   MRIwEAYDVQQIDAlTb21lU3RhdGUxETAPBgNVBAcMCFNvbWVDaXR5MRkwFwYDVQQK
   DBBTb21lT3JnYW5pemF0aW9uMR8wHQYDVQQLDBZTb21lT3JnYW5pemF0aW9uYWxV
   bml0MRkwFwYDVQQDDBBpcC0xNzItMzEtMjAtMjM2MSQwIgYJKoZIhvcNAQkBFhVy
   ...
   z5rRUE/XzxRLBZOoWZpNWTXJkQ3uFYH6s/sBwtHpKKZMzOvDedREjNKAvk4ws6F0
   CuIjvubtUysVyQoMVPQ97ldeakHWeRMiEJFXg6kZZ0vrGvwnKoMh3DlK44D9dlU3
   WanXWehT6FiSZvB4sTEXXJN2jdw8g+sHGnZ8zCOsclknYhHrCVD2vnBlZJKSZvak
   3ZazhBxtQSukFMOnWPP2a0DMMFGYUHOd0BQE8sBJxg==
   -----END CERTIFICATE-----
   ```

   The file names and extensions are a convenience and have no effect on function. For example, you can call a certificate `cert.crt`, `cert.pem`, or any other file name, so long as the related directive in the `ssl.conf` file uses the same name.
**Note**  
When you replace the default TLS files with your own customized files, be sure that they are in PEM format. 

1. Open the `/etc/httpd/conf.d/ssl.conf` file using your favorite text editor (such as **vim** or **nano**) as root user and comment out the following line, because the self-signed dummy certificate also contains the key. If you do not comment out this line before you complete the next step, the Apache service fails to start.

   ```
   SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
   ```

1. Restart Apache.

   ```
   [ec2-user ~]$ sudo systemctl restart httpd
   ```
**Note**  
Make sure that TCP port 443 is accessible on your EC2 instance, as previously described.

1. Your Apache web server should now support HTTPS (secure HTTP) over port 443. Test it by entering the IP address or fully qualified domain name of your EC2 instance into a browser URL bar with the prefix **https://**.

   Because you are connecting to a site with a self-signed, untrusted host certificate, your browser may display a series of security warnings. Override the warnings and proceed to the site. 

   If the default Apache test page opens, it means that you have successfully configured TLS on your server. All data passing between the browser and server is now encrypted.
**Note**  
To prevent site visitors from encountering warning screens, you must obtain a trusted, CA-signed certificate that not only encrypts, but also publicly authenticates you as the owner of the site. 

## Step 2: Obtain a CA-signed certificate
<a name="ssl_certificate"></a>

You can use the following process to obtain a CA-signed certificate:
+ Generate a certificate signing request (CSR) from a private key
+ Submit the CSR to a certificate authority (CA)
+ Obtain a signed host certificate
+ Configure Apache to use the certificate

A self-signed TLS X.509 host certificate is cryptologically identical to a CA-signed certificate. The difference is social, not mathematical. A CA promises, at a minimum, to validate a domain's ownership before issuing a certificate to an applicant. Each web browser contains a list of CAs trusted by the browser vendor to do this. An X.509 certificate consists primarily of a public key that corresponds to your private server key, and a signature by the CA that is cryptographically tied to the public key. When a browser connects to a web server over HTTPS, the server presents a certificate for the browser to check against its list of trusted CAs. If the signer is on the list, or accessible through a *chain of trust* consisting of other trusted signers, the browser negotiates a fast encrypted data channel with the server and loads the page. 

Certificates generally cost money because of the labor involved in validating the requests, so it pays to shop around. A few CAs offer basic-level certificates free of charge. The most notable of these CAs is the [Let's Encrypt](https://letsencrypt.org/) project, which also supports the automation of the certificate creation and renewal process. For more information about using a Let's Encrypt certificate, see [Get Certbot](https://eff-certbot.readthedocs.io/en/stable/install.html).

If you plan to offer commercial-grade services, [AWS Certificate Manager](https://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html) is a good option.

Underlying the host certificate is the key. As of 2019, [government](http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r4.pdf) and [industry](https://cabforum.org/wp-content/uploads/CA-Browser-Forum-BR-1.6.5.pdf) groups recommend using a minimum key (modulus) size of 2048 bits for RSA keys intended to protect documents, through 2030. The default modulus size generated by OpenSSL in AL2 is 2048 bits, which is suitable for use in a CA-signed certificate. In the following procedure, an optional step provided for those who want a customized key, for example, one with a larger modulus or using a different encryption algorithm.

**Important**  
These instructions for acquiring a CA-signed host certificate do not work unless you own a registered and hosted DNS domain.

**To obtain a CA-signed certificate**

1.  [Connect](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html) to your instance and navigate to /etc/pki/tls/private/. This is the directory where you store the server's private key for TLS. If you prefer to use an existing host key to generate the CSR, skip to Step 3.

1. (Optional) Generate a new private key. Here are some examples of key configurations. Any of the resulting keys works with your web server, but they vary in the degree and type of security that they implement.
   + **Example 1:** Create a default RSA host key. The resulting file, **custom.key**, is a 2048-bit RSA private key.

     ```
     [ec2-user ~]$ sudo openssl genrsa -out custom.key
     ```
   + **Example 2:** Create a stronger RSA key with a bigger modulus. The resulting file, **custom.key**, is a 4096-bit RSA private key.

     ```
     [ec2-user ~]$ sudo openssl genrsa -out custom.key 4096
     ```
   + **Example 3:** Create a 4096-bit encrypted RSA key with password protection. The resulting file, **custom.key**, is a 4096-bit RSA private key encrypted with the AES-128 cipher.
**Important**  
Encrypting the key provides greater security, but because an encrypted key requires a password, services depending on it cannot be auto-started. Each time you use this key, you must supply the password (in the preceding example, "abcde12345") over an SSH connection.

     ```
     [ec2-user ~]$ sudo openssl genrsa -aes128 -passout pass:abcde12345 -out custom.key 4096
     ```
   + **Example 4:** Create a key using a non-RSA cipher. RSA cryptography can be relatively slow because of the size of its public keys, which are based on the product of two large prime numbers. However, it is possible to create keys for TLS that use non-RSA ciphers. Keys based on the mathematics of elliptic curves are smaller and computationally faster when delivering an equivalent level of security.

     ```
     [ec2-user ~]$ sudo openssl ecparam -name prime256v1 -out custom.key -genkey
     ```

     The result is a 256-bit elliptic curve private key using prime256v1, a "named curve" that OpenSSL supports. Its cryptographic strength is slightly greater than a 2048-bit RSA key, [according to NIST](http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r4.pdf).
**Note**  
Not all CAs provide the same level of support for elliptic-curve-based keys as for RSA keys.

   Make sure that the new private key has highly restrictive ownership and permissions (owner=root, group=root, read/write for owner only). The commands would be as shown in the following example.

   ```
   [ec2-user ~]$ sudo chown root:root custom.key
   [ec2-user ~]$ sudo chmod 600 custom.key
   [ec2-user ~]$ ls -al custom.key
   ```

   The preceding commands yield the following result.

   ```
   -rw------- root root custom.key
   ```

    After you have created and configured a satisfactory key, you can create a CSR. 

1. Create a CSR using your preferred key. The following example uses **custom.key**.

   ```
   [ec2-user ~]$ sudo openssl req -new -key custom.key -out csr.pem
   ```

   OpenSSL opens a dialog and prompts you for the information shown in the following table. All of the fields except **Common Name** are optional for a basic, domain-validated host certificate.    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/linux/al2/ug/SSL-on-amazon-linux-2.html)

   Finally, OpenSSL prompts you for an optional challenge password. This password applies only to the CSR and to transactions between you and your CA, so follow the CA's recommendations about this and the other optional field, optional company name. The CSR challenge password has no effect on server operation.

   The resulting file **csr.pem** contains your public key, your digital signature of your public key, and the metadata that you entered.

1. Submit the CSR to a CA. This usually consists of opening your CSR file in a text editor and copying the contents into a web form. At this time, you may be asked to supply one or more subject alternate names (SANs) to be placed on the certificate. If **www.example.com** is the common name, then **example.com** would be a good SAN, and vice versa. A visitor to your site entering either of these names would see an error-free connection. If your CA web form allows it, include the common name in the list of SANs. Some CAs include it automatically.

   After your request has been approved, you receive a new host certificate signed by the CA. You might also be instructed to download an *intermediate certificate* file that contains additional certificates needed to complete the CA's chain of trust. 
**Note**  
Your CA might send you files in multiple formats intended for various purposes. For this tutorial, you should only use a certificate file in PEM format, which is usually (but not always) marked with a `.pem` or `.crt` file extension. If you are uncertain which file to use, open the files with a text editor and find the one containing one or more blocks beginning with the following line.  

   ```
   - - - - -BEGIN CERTIFICATE - - - - - 
   ```
The file should also end with the following line.  

   ```
   - - - -END CERTIFICATE - - - - -
   ```
You can also test the file at the command line as shown in the following.  

   ```
   [ec2-user certs]$ openssl x509 -in certificate.crt -text
   ```
Verify that these lines appear in the file. Do not use files ending with `.p7b`, `.p7c`, or similar file extensions.

1. Place the new CA-signed certificate and any intermediate certificates in the `/etc/pki/tls/certs` directory.
**Note**  
There are several ways to upload your new certificate to your EC2 instance, but the most straightforward and informative way is to open a text editor (for example, vi, nano, or notepad) on both your local computer and your instance, and then copy and paste the file contents between them. You need root [sudo] permissions when performing these operations on the EC2 instance. This way, you can see immediately if there are any permission or path problems. Be careful, however, not to add any additional lines while copying the contents, or to change them in any way. 

   From inside the `/etc/pki/tls/certs` directory, check that the file ownership, group, and permission settings match the highly restrictive AL2 defaults (owner=root, group=root, read/write for owner only). The following example shows the commands to use. 

   ```
   [ec2-user certs]$ sudo chown root:root custom.crt
   [ec2-user certs]$ sudo chmod 600 custom.crt
   [ec2-user certs]$ ls -al custom.crt
   ```

   These commands should yield the following result. 

   ```
   -rw------- root root custom.crt
   ```

   The permissions for the intermediate certificate file are less stringent (owner=root, group=root, owner can write, group can read, world can read). The following example shows the commands to use. 

   ```
   [ec2-user certs]$ sudo chown root:root intermediate.crt
   [ec2-user certs]$ sudo chmod 644 intermediate.crt
   [ec2-user certs]$ ls -al intermediate.crt
   ```

   These commands should yield the following result.

   ```
   -rw-r--r-- root root intermediate.crt
   ```

1. Place the private key that you used to create the CSR in the `/etc/pki/tls/private/` directory. 
**Note**  
There are several ways to upload your custom key to your EC2 instance, but the most straightforward and informative way is to open a text editor (for example, vi, nano, or notepad) on both your local computer and your instance, and then copy and paste the file contents between them. You need root [sudo] permissions when performing these operations on the EC2 instance. This way, you can see immediately if there are any permission or path problems. Be careful, however, not to add any additional lines while copying the contents, or to change them in any way.

   From inside the `/etc/pki/tls/private` directory, use the following commands to verify that the file ownership, group, and permission settings match the highly restrictive AL2 defaults (owner=root, group=root, read/write for owner only).

   ```
   [ec2-user private]$ sudo chown root:root custom.key
   [ec2-user private]$ sudo chmod 600 custom.key
   [ec2-user private]$ ls -al custom.key
   ```

   These commands should yield the following result.

   ```
   -rw------- root root custom.key
   ```

1. Edit `/etc/httpd/conf.d/ssl.conf` to reflect your new certificate and key files.

   1. Provide the path and file name of the CA-signed host certificate in Apache's `SSLCertificateFile` directive:

      ```
      SSLCertificateFile /etc/pki/tls/certs/custom.crt
      ```

   1. If you received an intermediate certificate file (`intermediate.crt` in this example), provide its path and file name using Apache's `SSLCACertificateFile` directive:

      ```
      SSLCACertificateFile /etc/pki/tls/certs/intermediate.crt
      ```
**Note**  
Some CAs combine the host certificate and the intermediate certificates in a single file, making the `SSLCACertificateFile` directive unnecessary. Consult the instructions provided by your CA.

   1. Provide the path and file name of the private key (`custom.key` in this example) in Apache's `SSLCertificateKeyFile` directive:

      ```
      SSLCertificateKeyFile /etc/pki/tls/private/custom.key
      ```

1. Save `/etc/httpd/conf.d/ssl.conf` and restart Apache.

   ```
   [ec2-user ~]$ sudo systemctl restart httpd
   ```

1. Test your server by entering your domain name into a browser URL bar with the prefix `https://`. Your browser should load the test page over HTTPS without generating errors.

## Step 3: Test and harden the security configuration
<a name="ssl_test"></a>

After your TLS is operational and exposed to the public, you should test how secure it really is. This is easy to do using online services such as [Qualys SSL Labs](https://www.ssllabs.com/ssltest/analyze.html), which performs a free and thorough analysis of your security setup. Based on the results, you may decide to harden the default security configuration by controlling which protocols you accept, which ciphers you prefer, and which you exclude. For more information, see [how Qualys formulates its scores](https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide).

**Important**  
Real-world testing is crucial to the security of your server. Small configuration errors may lead to serious security breaches and loss of data. Because recommended security practices change constantly in response to research and emerging threats, periodic security audits are essential to good server administration. 

On the [Qualys SSL Labs](https://www.ssllabs.com/ssltest/analyze.html) site, enter the fully qualified domain name of your server, in the form **www.example.com**. After about two minutes, you receive a grade (from A to F) for your site and a detailed breakdown of the findings. The following table summarizes the report for a domain with settings identical to the default Apache configuration on AL2, and with a default Certbot certificate. 


|  |  | 
| --- |--- |
| Overall rating | B | 
| Certificate | 100% | 
| Protocol support | 95% | 
| Key exchange | 70% | 
| Cipher strength | 90% | 

Though the overview shows that the configuration is mostly sound, the detailed report flags several potential problems, listed here in order of severity:

✗ **The RC4 cipher is supported for use by certain older browsers.** A cipher is the mathematical core of an encryption algorithm. RC4, a fast cipher used to encrypt TLS data-streams, is known to have several [serious weaknesses](http://www.imperva.com/docs/hii_attacking_ssl_when_using_rc4.pdf). Unless you have very good reasons to support legacy browsers, you should disable this.

✗ **Old TLS versions are supported.** The configuration supports TLS 1.0 (already deprecated) and TLS 1.1 (on a path to deprecation). Only TLS 1.2 has been recommended since 2018.

✗ **Forward secrecy is not fully supported.** [Forward secrecy](https://en.wikipedia.org/wiki/Forward_secrecy) is a feature of algorithms that encrypt using temporary (ephemeral) session keys derived from the private key. This means in practice that attackers cannot decrypt HTTPS data even if they possess a web server's long-term private key.

**To correct and future-proof the TLS configuration**

1. Open the configuration file `/etc/httpd/conf.d/ssl.conf` in a text editor and comment out the following line by entering "\$1" at the beginning of the line.

   ```
   #SSLProtocol all -SSLv3
   ```

1. Add the following directive:

   ```
   #SSLProtocol all -SSLv3
   SSLProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2
   ```

   This directive explicitly disables SSL versions 2 and 3, as well as TLS versions 1.0 and 1.1. The server now refuses to accept encrypted connections with clients using anything except TLS 1.2. The verbose wording in the directive conveys more clearly, to a human reader, what the server is configured to do.
**Note**  
Disabling TLS versions 1.0 and 1.1 in this manner blocks a small percentage of outdated web browsers from accessing your site.

**To modify the list of allowed ciphers**

1. In the configuration file `/etc/httpd/conf.d/ssl.conf`, find the section with the **SSLCipherSuite** directive and comment out the existing line by entering "\$1" at the beginning of the line.

   ```
   #SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5
   ```

1. Specify explicit cipher suites and a cipher order that prioritizes forward secrecy and avoids insecure ciphers. The `SSLCipherSuite` directive used here is based on output from the [Mozilla SSL Configuration Generator](https://mozilla.github.io/server-side-tls/ssl-config-generator/), which tailors a TLS configuration to the specific software running on your server. First determine your Apache and OpenSSL versions by using the output from the following commands.

   ```
   [ec2-user ~]$ yum list installed | grep httpd
   
   [ec2-user ~]$ yum list installed | grep openssl
   ```

   For example, if the returned information is Apache 2.4.34 and OpenSSL 1.0.2, we enter this into the generator. If you choose the "modern" compatibility model, this creates an `SSLCipherSuite` directive that aggressively enforces security but still works for most browsers. If your software doesn't support the modern configuration, you can update your software or choose the "intermediate" configuration instead.

   ```
   SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:
   ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:
   ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
   ```

   The selected ciphers have *ECDHE* in their names, an abbreviation for *Elliptic Curve Diffie-Hellman Ephemeral *. The term *ephemeral* indicates forward secrecy. As a by-product, these ciphers do not support RC4.

   We recommend that you use an explicit list of ciphers instead of relying on defaults or terse directives whose content isn't visible.

   Copy the generated directive into `/etc/httpd/conf.d/ssl.conf`.
**Note**  
Though shown here on several lines for readability, the directive must be on a single line when copied to `/etc/httpd/conf.d/ssl.conf`, with only a colon (no spaces) between cipher names.

1. Finally, uncomment the following line by removing the "\$1" at the beginning of the line.

   ```
   #SSLHonorCipherOrder on
   ```

   This directive forces the server to prefer high-ranking ciphers, including (in this case) those that support forward secrecy. With this directive turned on, the server tries to establish a strong secure connection before falling back to allowed ciphers with lesser security.

After completing both of these procedures, save the changes to `/etc/httpd/conf.d/ssl.conf` and restart Apache.

If you test the domain again on [Qualys SSL Labs](https://www.ssllabs.com/ssltest/analyze.html), you should see that the RC4 vulnerability and other warnings are gone and the summary looks something like the following.


|  |  | 
| --- |--- |
| Overall rating | A | 
| Certificate | 100% | 
| Protocol support | 100% | 
| Key exchange | 90% | 
| Cipher strength | 90% | 

Each update to OpenSSL introduces new ciphers and removes support for old ones. Keep your EC2 AL2 instance up-to-date, watch for security announcements from [OpenSSL](https://www.openssl.org/), and be alert to reports of new security exploits in the technical press.

## Troubleshoot
<a name="troubleshooting"></a>
+ **My Apache webserver doesn't start unless I enter a password**

  This is expected behavior if you installed an encrypted, password-protected, private server key.

  You can remove the encryption and password requirement from the key. Assuming that you have a private encrypted RSA key called `custom.key` in the default directory, and that the password on it is **abcde12345**, run the following commands on your EC2 instance to generate an unencrypted version of the key.

  ```
  [ec2-user ~]$ cd /etc/pki/tls/private/
  [ec2-user private]$ sudo cp custom.key custom.key.bak
  [ec2-user private]$ sudo openssl rsa -in custom.key -passin pass:abcde12345 -out custom.key.nocrypt 
  [ec2-user private]$ sudo mv custom.key.nocrypt custom.key
  [ec2-user private]$ sudo chown root:root custom.key
  [ec2-user private]$ sudo chmod 600 custom.key
  [ec2-user private]$ sudo systemctl restart httpd
  ```

  Apache should now start without prompting you for a password.
+  **I get errors when I run sudo yum install -y mod\$1ssl.**

  When you are installing the required packages for SSL, you may see errors similar to the following.

  ```
  Error: httpd24-tools conflicts with httpd-tools-2.2.34-1.16.amzn1.x86_64
  Error: httpd24 conflicts with httpd-2.2.34-1.16.amzn1.x86_64
  ```

  This typically means that your EC2 instance is not running AL2. This tutorial only supports instances freshly created from an official AL2 AMI.

# Tutorial: Host a WordPress blog on AL2
<a name="hosting-wordpress"></a>

The following procedures will help you install, configure, and secure a WordPress blog on your AL2 instance. This tutorial is a good introduction to using Amazon EC2 in that you have full control over a web server that hosts your WordPress blog, which is not typical with a traditional hosting service.

You are responsible for updating the software packages and maintaining security patches for your server. For a more automated WordPress installation that does not require direct interaction with the web server configuration, the CloudFormation service provides a WordPress template that can also get you started quickly. For more information, see [Get started](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/GettingStarted.Walkthrough.html) in the *AWS CloudFormation User Guide*. If you need a high-availability solution with a decoupled database, see [Deploying a high-availability WordPress website](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/php-hawordpress-tutorial.html) in the *AWS Elastic Beanstalk Developer Guide*.

**Important**  
These procedures are intended for use with AL2. For more information about other distributions, see their specific documentation. Many steps in this tutorial do not work on Ubuntu instances. For help installing WordPress on an Ubuntu instance, see [WordPress](https://help.ubuntu.com/community/WordPress) in the Ubuntu documentation. You can also use [CodeDeploy](https://docs.aws.amazon.com/codedeploy/latest/userguide/tutorials-wordpress-launch-instance.html) to accomplish this task on Amazon Linux, macOS, or Unix systems.

**Topics**
+ [Prerequisites](#hosting-wordpress-prereqs)
+ [Install WordPress](#install-wordpress)
+ [Next steps](#wordpress-next-steps)
+ [Help\$1 My public DNS name changed and now my blog is broken](#wordpress-troubleshooting)

## Prerequisites
<a name="hosting-wordpress-prereqs"></a>

This tutorial assumes that you have launched an AL2 instance with a functional web server with PHP and database (either MySQL or MariaDB) support by following all of the steps in [Tutorial: Install a LAMP server on AL2](ec2-lamp-amazon-linux-2.md). This tutorial also has steps for configuring a security group to allow `HTTP` and `HTTPS` traffic, as well as several steps to ensure that file permissions are set properly for your web server. For information about adding rules to your security group, see [Add rules to a security group](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-security-groups.html#adding-security-group-rule).

We strongly recommend that you associate an Elastic IP address (EIP) to the instance you are using to host a WordPress blog. This prevents the public DNS address for your instance from changing and breaking your installation. If you own a domain name and you want to use it for your blog, you can update the DNS record for the domain name to point to your EIP address (for help with this, contact your domain name registrar). You can have one EIP address associated with a running instance at no charge. For more information, see [Elastic IP addresses](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html) in the *Amazon EC2 User Guide*.

If you don't already have a domain name for your blog, you can register a domain name with Route 53 and associate your instance's EIP address with your domain name. For more information, see [Registering domain names using Amazon Route 53](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/registrar.html) in the *Amazon Route 53 Developer Guide*.

## Install WordPress
<a name="install-wordpress"></a>

**Option: Complete this tutorial using automation**  
To complete this tutorial using AWS Systems Manager Automation instead of the following tasks, run the [automation document](https://console.aws.amazon.com/systems-manager/documents/AWSDocs-HostingAWordPressBlog/).

Connect to your instance, and download the WordPress installation package.

**To download and unzip the WordPress installation package**

1. Download the latest WordPress installation package with the **wget** command. The following command should always download the latest release.

   ```
   [ec2-user ~]$ wget https://wordpress.org/latest.tar.gz
   ```

1. Unzip and unarchive the installation package. The installation folder is unzipped to a folder called `wordpress`.

   ```
   [ec2-user ~]$ tar -xzf latest.tar.gz
   ```<a name="create_user_and_database"></a>

**To create a database user and database for your WordPress installation**

Your WordPress installation needs to store information, such as blog posts and user comments, in a database. This procedure helps you create your blog's database and a user that is authorized to read and save information to it. 

1. Start the database server.
   + 

     ```
     [ec2-user ~]$ sudo systemctl start mariadb
     ```

1. Log in to the database server as the `root` user. Enter your database `root` password when prompted; this may be different than your `root` system password, or it might even be empty if you have not secured your database server.

   If you have not secured your database server yet, it is important that you do so. For more information, see [To secure the MariaDB server](ec2-lamp-amazon-linux-2.md#securing-maria-db) (AL2).

   ```
   [ec2-user ~]$ mysql -u root -p
   ```

1. <a name="create_database_user"></a>Create a user and password for your MySQL database. Your WordPress installation uses these values to communicate with your MySQL database. 

   Make sure that you create a strong password for your user. Do not use the single quote character ( ' ) in your password, because this will break the preceding command. Do not reuse an existing password, and make sure to store this password in a safe place.

   Enter the following command, substituting a unique user name and password.

   ```
   CREATE USER 'wordpress-user'@'localhost' IDENTIFIED BY 'your_strong_password';
   ```

1. <a name="create_database"></a>Create your database. Give your database a descriptive, meaningful name, such as `wordpress-db`.
**Note**  
The punctuation marks surrounding the database name in the command below are called backticks. The backtick (```) key is usually located above the `Tab` key on a standard keyboard. Backticks are not always required, but they allow you to use otherwise illegal characters, such as hyphens, in database names.

   ```
   CREATE DATABASE `wordpress-db`;
   ```

1. Grant full privileges for your database to the WordPress user that you created earlier.

   ```
   GRANT ALL PRIVILEGES ON `wordpress-db`.* TO "wordpress-user"@"localhost";
   ```

1. Flush the database privileges to pick up all of your changes.

   ```
   FLUSH PRIVILEGES;
   ```

1. Exit the `mysql` client.

   ```
   exit
   ```

**To create and edit the wp-config.php file**

The WordPress installation folder contains a sample configuration file called `wp-config-sample.php`. In this procedure, you copy this file and edit it to fit your specific configuration.

1. Copy the `wp-config-sample.php` file to a file called `wp-config.php`. This creates a new configuration file and keeps the original sample file intact as a backup.

   ```
   [ec2-user ~]$ cp wordpress/wp-config-sample.php wordpress/wp-config.php
   ```

1. Edit the `wp-config.php` file with your favorite text editor (such as **nano** or **vim**) and enter values for your installation. If you do not have a favorite text editor, `nano` is suitable for beginners.

   ```
   [ec2-user ~]$ nano wordpress/wp-config.php
   ```

   1. Find the line that defines `DB_NAME` and change `database_name_here` to the database name that you created in [Step 4](#create_database) of [To create a database user and database for your WordPress installation](#create_user_and_database).

      ```
      define('DB_NAME', 'wordpress-db');
      ```

   1. Find the line that defines `DB_USER` and change `username_here` to the database user that you created in [Step 3](#create_database_user) of [To create a database user and database for your WordPress installation](#create_user_and_database).

      ```
      define('DB_USER', 'wordpress-user');
      ```

   1. Find the line that defines `DB_PASSWORD` and change `password_here` to the strong password that you created in [Step 3](#create_database_user) of [To create a database user and database for your WordPress installation](#create_user_and_database).

      ```
      define('DB_PASSWORD', 'your_strong_password');
      ```

   1. Find the section called `Authentication Unique Keys and Salts`. These `KEY` and `SALT` values provide a layer of encryption to the browser cookies that WordPress users store on their local machines. Basically, adding long, random values here makes your site more secure. Visit [https://api.wordpress.org/secret-key/1.1/salt/](https://api.wordpress.org/secret-key/1.1/salt/) to randomly generate a set of key values that you can copy and paste into your `wp-config.php` file. To paste text into a PuTTY terminal, place the cursor where you want to paste the text and right-click your mouse inside the PuTTY terminal.

      For more information about security keys, go to [https://wordpress.org/support/article/editing-wp-config-php/\$1security-keys](https://wordpress.org/support/article/editing-wp-config-php/#security-keys).
**Note**  
The values below are for example purposes only; do not use these values for your installation.

      ```
      define('AUTH_KEY',         ' #U$$+[RXN8:b^-L 0(WU_+ c+WFkI~c]o]-bHw+)/Aj[wTwSiZ<Qb[mghEXcRh-');
      define('SECURE_AUTH_KEY',  'Zsz._P=l/|y.Lq)XjlkwS1y5NJ76E6EJ.AV0pCKZZB,*~*r ?6OP$eJT@;+(ndLg');
      define('LOGGED_IN_KEY',    'ju}qwre3V*+8f_zOWf?{LlGsQ]Ye@2Jh^,8x>)Y |;(^[Iw]Pi+LG#A4R?7N`YB3');
      define('NONCE_KEY',        'P(g62HeZxEes|LnI^i=H,[XwK9I&[2s|:?0N}VJM%?;v2v]v+;+^9eXUahg@::Cj');
      define('AUTH_SALT',        'C$DpB4Hj[JK:?{ql`sRVa:{:7yShy(9A@5wg+`JJVb1fk%_-Bx*M4(qc[Qg%JT!h');
      define('SECURE_AUTH_SALT', 'd!uRu#}+q#{f$Z?Z9uFPG.${+S{n~1M&%@~gL>U>NV<zpD-@2-Es7Q1O-bp28EKv');
      define('LOGGED_IN_SALT',   ';j{00P*owZf)kVD+FVLn-~ >.|Y%Ug4#I^*LVd9QeZ^&XmK|e(76miC+&W&+^0P/');
      define('NONCE_SALT',       '-97r*V/cgxLmp?Zy4zUU4r99QQ_rGs2LTd%P;|_e1tS)8_B/,.6[=UK<J_y9?JWG');
      ```

   1. Save the file and exit your text editor.

**To install your WordPress files under the Apache document root**
+ Now that you've unzipped the installation folder, created a MySQL database and user, and customized the WordPress configuration file, you are ready to copy your installation files to your web server document root so you can run the installation script that completes your installation. The location of these files depends on whether you want your WordPress blog to be available at the actual root of your web server (for example, `my.public.dns.amazonaws.com`) or in a subdirectory or folder under the root (for example, `my.public.dns.amazonaws.com/blog`).
  + If you want WordPress to run at your document root, copy the contents of the wordpress installation directory (but not the directory itself) as follows: 

    ```
    [ec2-user ~]$ cp -r wordpress/* /var/www/html/
    ```
  + If you want WordPress to run in an alternative directory under the document root, first create that directory, and then copy the files to it. In this example, WordPress will run from the directory `blog`:

    ```
    [ec2-user ~]$ mkdir /var/www/html/blog
    [ec2-user ~]$ cp -r wordpress/* /var/www/html/blog/
    ```

**Important**  
For security purposes, if you are not moving on to the next procedure immediately, stop the Apache web server (`httpd`) now. After you move your installation under the Apache document root, the WordPress installation script is unprotected and an attacker could gain access to your blog if the Apache web server were running. To stop the Apache web server, enter the command **sudo systemctl stop httpd**. If you are moving on to the next procedure, you do not need to stop the Apache web server.

**To allow WordPress to use permalinks**

WordPress permalinks need to use Apache `.htaccess` files to work properly, but this is not enabled by default on Amazon Linux. Use this procedure to allow all overrides in the Apache document root.

1. Open the `httpd.conf` file with your favorite text editor (such as **nano** or **vim**). If you do not have a favorite text editor, `nano` is suitable for beginners.

   ```
   [ec2-user ~]$ sudo vim /etc/httpd/conf/httpd.conf
   ```

1. Find the section that starts with `<Directory "/var/www/html">`.

   ```
   <Directory "/var/www/html">
       #
       # Possible values for the Options directive are "None", "All",
       # or any combination of:
       #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
       #
       # Note that "MultiViews" must be named *explicitly* --- "Options All"
       # doesn't give it to you.
       #
       # The Options directive is both complicated and important.  Please see
       # http://httpd.apache.org/docs/2.4/mod/core.html#options
       # for more information.
       #
       Options Indexes FollowSymLinks
   
       #
       # AllowOverride controls what directives may be placed in .htaccess files.
       # It can be "All", "None", or any combination of the keywords:
       #   Options FileInfo AuthConfig Limit
       #
       AllowOverride None
   
       #
       # Controls who can get stuff from this server.
       #
       Require all granted
   </Directory>
   ```

1. Change the `AllowOverride None` line in the above section to read `AllowOverride All`.
**Note**  
There are multiple `AllowOverride` lines in this file; be sure you change the line in the `<Directory "/var/www/html">` section.

   ```
   AllowOverride All
   ```

1. Save the file and exit your text editor.

**To install the PHP graphics drawing library on AL2**  
The GD library for PHP enables you to modify images. Install this library if you need to crop the header image for your blog. The version of phpMyAdmin that you install might require a specific minimum version of this library (for example, version 7.2).

Use the following command to install the PHP graphics drawing library on AL2. For example, if you installed php7.2 from amazon-linux-extras as part of installing the LAMP stack, this command installs version 7.2 of the PHP graphics drawing library.

```
[ec2-user ~]$ sudo yum install php-gd
```

To verify the installed version, use the following command:

```
[ec2-user ~]$ sudo yum list installed php-gd
```

The following is example output:

```
php-gd.x86_64                     7.2.30-1.amzn2             @amzn2extra-php7.2
```

**To fix file permissions for the Apache web server**

Some of the available features in WordPress require write access to the Apache document root (such as uploading media though the Administration screens). If you have not already done so, apply the following group memberships and permissions (as described in greater detail in the [Tutorial: Install a LAMP server on AL2](ec2-lamp-amazon-linux-2.md)).

1. Grant file ownership of `/var/www` and its contents to the `apache` user.

   ```
   [ec2-user ~]$ sudo chown -R apache /var/www
   ```

1. Grant group ownership of `/var/www` and its contents to the `apache` group.

   ```
   [ec2-user ~]$ sudo chgrp -R apache /var/www
   ```

1. Change the directory permissions of `/var/www` and its subdirectories to add group write permissions and to set the group ID on future subdirectories.

   ```
   [ec2-user ~]$ sudo chmod 2775 /var/www
   [ec2-user ~]$ find /var/www -type d -exec sudo chmod 2775 {} \;
   ```

1. Recursively change the file permissions of `/var/www` and its subdirectories.

   ```
   [ec2-user ~]$ find /var/www -type f -exec sudo chmod 0644 {} \;
   ```
**Note**  
 If you intend to also use WordPress as an FTP server, you'll need more permissive Group settings here. Please review the recommended [steps and security settings in WordPress](https://wordpress.org/support/article/changing-file-permissions/) to accomplish this. 

1. Restart the Apache web server to pick up the new group and permissions.
   + 

     ```
     [ec2-user ~]$ sudo systemctl restart httpd
     ```

**Run the WordPress installation script with AL2**

You are ready to install WordPress. The commands that you use depend on the operating system. The commands in this procedure are for use with AL2.

1. Use the **systemctl** command to ensure that the `httpd` and database services start at every system boot.

   ```
   [ec2-user ~]$ sudo systemctl enable httpd && sudo systemctl enable mariadb
   ```

1. Verify that the database server is running.

   ```
   [ec2-user ~]$ sudo systemctl status mariadb
   ```

   If the database service is not running, start it.

   ```
   [ec2-user ~]$ sudo systemctl start mariadb
   ```

1. Verify that your Apache web server (`httpd`) is running.

   ```
   [ec2-user ~]$ sudo systemctl status httpd
   ```

   If the `httpd` service is not running, start it.

   ```
   [ec2-user ~]$ sudo systemctl start httpd
   ```

1. In a web browser, type the URL of your WordPress blog (either the public DNS address for your instance, or that address followed by the `blog` folder). You should see the WordPress installation script. Provide the information required by the WordPress installation. Choose **Install WordPress** to complete the installation. For more information, see [Step 5: Run the Install Script](https://wordpress.org/support/article/how-to-install-wordpress/#step-5-run-the-install-script) on the WordPress website.

## Next steps
<a name="wordpress-next-steps"></a>

After you have tested your WordPress blog, consider updating its configuration.

**Use a custom domain name**  
If you have a domain name associated with your EC2 instance's EIP address, you can configure your blog to use that name instead of the EC2 public DNS address. For more information, see [Changing The Site URL](https://wordpress.org/support/article/changing-the-site-url/) on the WordPress website.

**Configure your blog**  
You can configure your blog to use different [themes](https://wordpress.org/themes/) and [plugins](https://wordpress.org/plugins/) to offer a more personalized experience for your readers. However, sometimes the installation process can backfire, causing you to lose your entire blog. We strongly recommend that you create a backup Amazon Machine Image (AMI) of your instance before attempting to install any themes or plugins so you can restore your blog if anything goes wrong during installation. For more information, see [Create your own AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html#creating-an-ami).

**Increase capacity**  
If your WordPress blog becomes popular and you need more compute power or storage, consider the following steps:
+ Expand the storage space on your instance. For more information, see [Amazon EBS Elastic Volumes](https://docs.aws.amazon.com/ebs/latest/userguide/ebs-modify-volume.html) in the *Amazon EBS User Guide*.
+ Move your MySQL database to [Amazon RDS](https://aws.amazon.com/rds) to take advantage of the service's ability to scale easily.

**Improve network performance of your internet traffic**  
If you expect your blog to drive traffic from users located around the world, consider [AWS Global Accelerator](https://aws.amazon.com/global-accelerator). Global Accelerator helps you achieve lower latency by improving internet traffic performance between your users’ client devices and your WordPress application running on AWS. Global Accelerator uses the [AWS global network](https://aws.amazon.com/about-aws/global-infrastructure/global_network/) to direct traffic to a healthy application endpoint in the AWS Region that is closest to the client.

**Learn more about WordPress**  
For information about WordPress, see the WordPress Codex help documentation at [http://codex.wordpress.org/](http://codex.wordpress.org/).

For more information about troubleshooting your installation, see [Common installation problems](https://wordpress.org/support/article/how-to-install-wordpress/#common-installation-problems).

For information about making your WordPress blog more secure, see [Hardening WordPress](https://wordpress.org/support/article/hardening-wordpress/).

For information about keeping your WordPress blog up-to-date, see [Updating WordPress](https://wordpress.org/support/article/updating-wordpress/).

## Help\$1 My public DNS name changed and now my blog is broken
<a name="wordpress-troubleshooting"></a>

Your WordPress installation is automatically configured using the public DNS address for your EC2 instance. If you stop and restart the instance, the public DNS address changes (unless it is associated with an Elastic IP address) and your blog will not work anymore because it references resources at an address that no longer exists (or is assigned to another EC2 instance). A more detailed description of the problem and several possible solutions are outlined in [Changing the Site URL](https://wordpress.org/support/article/changing-the-site-url/).

If this has happened to your WordPress installation, you might be able to recover your blog with the procedure below, which uses the **wp-cli** command line interface for WordPress.

**To change your WordPress site URL with the **wp-cli****

1. Connect to your EC2 instance with SSH. 

1. Note the old site URL and the new site URL for your instance. The old site URL is likely the public DNS name for your EC2 instance when you installed WordPress. The new site URL is the current public DNS name for your EC2 instance. If you are not sure of your old site URL, you can use **curl** to find it with the following command.

   ```
   [ec2-user ~]$ curl localhost | grep wp-content
   ```

   You should see references to your old public DNS name in the output, which will look like this (old site URL in red):

   ```
   <script type='text/javascript' src='http://ec2-52-8-139-223.us-west-1.compute.amazonaws.com/wp-content/themes/twentyfifteen/js/functions.js?ver=20150330'></script>
   ```

1. Download the **wp-cli** with the following command.

   ```
   [ec2-user ~]$ curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
   ```

1. Search and replace the old site URL in your WordPress installation with the following command. Substitute the old and new site URLs for your EC2 instance and the path to your WordPress installation (usually `/var/www/html` or `/var/www/html/blog`).

   ```
   [ec2-user ~]$ php wp-cli.phar search-replace 'old_site_url' 'new_site_url' --path=/path/to/wordpress/installation --skip-columns=guid
   ```

1. In a web browser, enter the new site URL of your WordPress blog to verify that the site is working properly again. If it is not, see [Changing the Site URL](https://wordpress.org/support/article/changing-the-site-url/) and [Common installation problems](https://wordpress.org/support/article/how-to-install-wordpress/#common-installation-problems) for more information.