Website on Nginx webserver with ssl on EC2

How to Run a Website on Nginx Server with SSL on AWS EC2 Instance

AWS EC2 instances can be great for quickly launching websites and bringing them online. AWS offers EC2 instances of various types and in varying price ranges as well as capacity. Performance wise, these instances are fast and with NGINX installed on an EC2 instance with fastcgi caching, you can expect unmatched speed. To quickly launch a static html website, you just need to launch an instance, install the NGINX server, add the content (index.html) and apply ssl.

Let’s begin with the launch of an AWS EC2 instance of desired size. You can start with t2.micro which is also free tier eligible or with a somewhat larger instance like t2.small, medium or large. We are going to launch a t2.small instance with 1vCPU and 2gb memory as well as 10gb storage volume. You can launch an instance using either the AWS management console or the AWS Command Line Interface (CLI). If you have worked with the AWS CLI, it will not take you long to launch an instance. To do it through the CLI you need to install and configure it on your computer first. Let’s fist look at the process of launching an EC2 instance through the AWS management console. All you need right now is an AWS account. Login to your account and access the EC2 dashboard. From the dashboard, click on Launch Instances to go to the instance creation page.

Let’s launch an AWS EC2 instance from the console:

                                          

The instance creation process begins with naming and tagging your instance. First give your instance a name and tag it. Suppose, we have named our instance My web server and assigned tags for owner and purpose of creating the instance. You can add up to 50 tags to an instance. These tags also help you organize your resources. To tag any instance, you fill in the key and value. For example, I added a tag for ownership where the key is owner and the value Team A.

After naming and tagging your instance, you can move on to selecting the Application and OS Images (Amazon Machine Image). In the quick start section, you will find some AMIs including Amazon Linux2, Mac OS, Windows, Ubuntu, SUSE Linux, Red Hat and Debian. We have selected the Amazon Linux2 instance for the current project. Each instance has an image id which displays below the image when you select it. For example, Amazon Linux 2 has the image id: ami-006dcf34c09e50022 and Ubuntu has ami-0557a15b87f6559cf. You will need the ami id if you plan to launch instances using the AWS CLI. However, you can also learn about these machine images using the CLI, which we will discuss later in this article.

Right now, let us just move on to selecting the instance type and creating a key pair. Both t1.micro and t2.micro are free tier eligible. However, we will select the t2.small instance with 1vCPU and 2GB RAM. You can get the information related to pricing, RAM sizes and CPU with the instance type which makes it easier for you to select the right instance for your project. After having selected the instance type, you will need to create a keypair that will be required to connect securely with your instance. Click on create new keypair to the right of the keypair box and give your keypair a name like ExampleKeyPair.

In the network settings, you need to work on the security groups or the firewall settings which will decide which type of incoming and outgoing traffic is allowed.  You will see the default VPC in the network settings. There is not much to do here except that you need to create a security group, and allow ssh, http and https traffic. These are the three main inbound rules you need to add right now. If you want to add more rules, you can do it by going to security groups in your console and creating a security group and add inbound and outbound rules.

In the last step, configure the storage and increase it to 20gb. Click on Launch instance at the bottom right. The instance creation process will begin and the instance will initialize a little later.

Once the instance is in running state and passed the 2/2 checks, you can connect to your instance via ssh.

                               

Create the instance using AWS CLI:

You will need AWS CLI installed and configure to launch EC2 instances using the CLI.

For launching an instance using the AWS CLI, you use the run-instances command as listed below:

$ aws ec2 run-instances \

–image-id ami-0abcdef1234567890 \

–instance-type t2.small \

–key-name MyKeyPair

So, you do not have much to do except that you have to select the right instance type and provide the image id for Amazon Machine image and the name of a keypair which you want to use with your instance. If you have all of these ready, you can just launch the instance like below (you can also add security group id and subnet id, or the system will use default security group, you can change security group later as well):

$ aws ec2 run-instances –image-id ami-006dcf34c09e50022 –instance-type t2.small –key-name ExampleKeyPair

When you run the above command, you will see an output like below:

{

“Groups”: [],

“Instances”: [

     {

         “AmiLaunchIndex”: 0,

         “ImageId”: “ami-006dcf34c09e50022”,

         “InstanceId”: “i-0c48d5b0138b85062”,  — Id of our newly created instance

         “InstanceType”: “t2.small”,                   The instance type

         “KeyName”: “ExampleKeyPair”,            The key pair

         “LaunchTime”: “2023-03-05T07:47:31+00:00”, Time of launch

         “Monitoring”: {

             “State”: “disabled”

         },

         “Placement”: {

             “AvailabilityZone”: “us-east-1b”,         (Availability zone)

             “GroupName”: “”,

             “Tenancy”: “default”

         },

         “PrivateDnsName”: “ip-172-31-00-000.ec2.internal”,

         “PrivateIpAddress”: “172.31.00.000”,

         “ProductCodes”: [],

         “PublicDnsName”: “”,

         “State”: {

            “Code”: 0,

             “Name”: “pending”

         },

         “StateTransitionReason”: “”,

         “SubnetId”: “subnet-b5dbc54fc”,

         “VpcId”: “vpc-36c8675d”,

         “Architecture”: “x86_64”,

         “BlockDeviceMappings”: [],

“ClientToken”: “2d777333-b7a7-455c-b33a-555cbfff0999”,

         “EbsOptimized”: false,

         “EnaSupport”: true,

         “Hypervisor”: “xen”,

         “NetworkInterfaces”: [

             {

                 “Attachment”: {

                     “AttachTime”: “2023-03-05T07:47:31+00:00”,

                     “AttachmentId”: “eni-attach-044cb777fbc9999e7”,

                     “DeleteOnTermination”: true,

                     “DeviceIndex”: 0,

                    “Status”: “attaching”,

                     “NetworkCardIndex”: 0

                 },

                 “Description”: “”,

                 “Groups”: [

                     {

                         “GroupName”: “default”,

                         “GroupId”: “sg-5827011d”

                     }

                 ],

                 “Ipv6Addresses”: [],

                 “MacAddress”: “0a:aa:71:67:cc:19”,

                 “NetworkInterfaceId”: “eni-001eb8f05dfb33e11”,

                 “OwnerId”: “33334488886”,

                 “PrivateDnsName”: “ip-172-31-00-000.ec2.internal”,

                 “PrivateIpAddress”: “172.31.00.000”,

                 “PrivateIpAddresses”: [

                     {

                         “Primary”: true,

                         “PrivateDnsName”: “ip-172-31-00-000.ec2.internal”,

                        “PrivateIpAddress”: “172.31.00.000”

                     }

                 ],

                 “SourceDestCheck”: true,

                 “Status”: “in-use”,

                 “SubnetId”: “subnet-b6fbc8fc”,

                 “VpcId”: “vpc-36c8675d”,

                 “InterfaceType”: “interface”

             }

         ],

         “RootDeviceName”: “/dev/xvda”,

         “RootDeviceType”: “ebs”,

         “SecurityGroups”: [

             {

                 “GroupName”: “default”,

                 “GroupId”: “sg-5827011d”

             }

         ],

         “SourceDestCheck”: true,

         “StateReason”: {

             “Code”: “pending”,

             “Message”: “pending”

         },

         “VirtualizationType”: “hvm”,

         “CpuOptions”: {

             “CoreCount”: 1,

             “ThreadsPerCore”: 1

         },

         “CapacityReservationSpecification”: {

             “CapacityReservationPreference”: “open”

         },

         “MetadataOptions”: {

             “State”: “pending”,

             “HttpTokens”: “optional”,

             “HttpPutResponseHopLimit”: 1,

             “HttpEndpoint”: “enabled”,

             “HttpProtocolIpv6”: “disabled”,

             “InstanceMetadataTags”: “disabled”

         },

         “EnclaveOptions”: {

             “Enabled”: false

         },

         “PrivateDnsNameOptions”: {

             “HostnameType”: “ip-name”,

             “EnableResourceNameDnsARecord”: false,

             “EnableResourceNameDnsAAAARecord”: false

         },

         “MaintenanceOptions”: {

             “AutoRecovery”: “default”

         }

     }

],

“OwnerId”: “302984893886”,

“ReservationId”: “r-0e995566116bebcc5”

}

So, your instance has been created and you can check it out from your console in the list of instances on your EC2 dashboard. You will find the instance id next to the instance name, if you have provided one, on your EC2 dashboard.

Now, that we have the instance ready, we can move on to install the NGINX server.

Install NGINX Server on EC2 instance:

After having launched your instance, wait for a few minutes before trying to ssh connect with it. If you face any difficulty establishing ssh connection, then check out the security group attached to your instance and edit the inbound rules. Allow ssh traffic from your ip or custom IP and then try again.

Once you are connected to your instance, you can start the installation of NGINX webserver.

  1. Enable the EPEL Repo for your Amazon Linux 2 EC2 instance:
$ sudo amazon-linux-extras enable epel

The output will list the packages available for installation :

0  ansible2             available    \

     [ =2.4.2  =2.4.6  =2.8  =stable ]

  2  httpd_modules        available [ =1.0  =stable ]

  3  memcached1.5         available \

     [ =1.5.1  =1.5.16  =1.5.17 ]

  6  postgresql10         available [ =10  =stable ]

  9  R3.4                 available [ =3.4.3  =stable ]

 10  rust1                available \

     [ =1.22.1  =1.26.0  =1.26.1  =1.27.2  =1.31.0  =1.38.0

       =stable ]

 18  libreoffice          available \

     [ =5.0.6.2_15  =5.3.6.1  =stable ]

 19  gimp                 available [ =2.8.22 ]

 20  docker=latest        enabled  \

     [ =17.12.1  =18.03.1  =18.06.1  =18.09.9  =stable ]

 21  mate-desktop1.x      available \

     [ =1.19.0  =1.20.0  =stable ]

 22  GraphicsMagick1.3    available \

     [ =1.3.29  =1.3.32  =1.3.34  =stable ]

 23  tomcat8.5            available \

     [ =8.5.31  =8.5.32  =8.5.38  =8.5.40  =8.5.42  =8.5.50

       =stable ]

 24  epel=latest          enabled  [ =7.11  =stable ]

 25  testing              available [ =1.0  =stable ]

 26  ecs                  available [ =stable ]

 27  corretto8            available \

     [ =1.8.0_192  =1.8.0_202  =1.8.0_212  =1.8.0_222  =1.8.0_232

       =1.8.0_242  =stable ]

 29  golang1.11           available \

     [ =1.11.3  =1.11.11  =1.11.13  =stable ]

 30  squid4               available [ =4  =stable ]

 32  lustre2.10           available \

     [ =2.10.5  =2.10.8  =stable ]

 33  java-openjdk11       available [ =11  =stable ]

 34  lynis                available [ =stable ]

 36  BCC                  available [ =0.x  =stable ]

 37  mono                 available [ =5.x  =stable ]

 38  nginx1               available [ =stable ]

 39  ruby2.6              available [ =2.6  =stable ]

 40  mock                 available [ =stable ]

 41  postgresql11         available [ =11  =stable ]

 43  livepatch            available [ =stable ]

 44  python3.8            available [ =stable ]

 45  haproxy2             available [ =stable ]

 46  collectd             available [ =stable ]

 47  aws-nitro-enclaves-cli   available [ =stable ]

 48  R4                   available [ =stable ]

  _  kernel-5.4           available [ =stable ]

 50  selinux-ng           available [ =stable ]

 51  php8.0               available [ =stable ]

 52  tomcat9              available [ =stable ]

 53  unbound1.13          available [ =stable ]

 54  mariadb10.5              available [ =stable ]

 55  kernel-5.10=latest   enabled  [ =stable ]

 56  redis6               available [ =stable ]

 57  ruby3.0              available [ =stable ]

 58  postgresql12         available [ =stable ]

 59  postgresql13         available [ =stable ]

 60  mock2                available [ =stable ]

 61  dnsmasq2.85          available [ =stable ]

 62  kernel-5.15          available [ =stable ]

 63  postgresql14         available [ =stable ]

 64  firefox              available [ =stable ]

 65  lustre               available [ =stable ]

 66  php8.1               available [ =stable ]

 67  awscli1              available [ =stable ]

  1. Now, install the EPEL Repo:
$ sudo yum clean metadata
$ sudo yum install epel-release
  1. Now install nginx:
$ sudo yum install nginx
  1. Start and enable NGINX:
$ sudo systemctl start nginx
$ sudo systemctl enable nginx

Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.

  1. Check nginx status:
$ sudo systemctl status nginx

The following output shows that nginx server is active and running on your AWS EC2 instance:

nginx.service – The nginx HTTP and reverse proxy server

   Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)

   Active: active (running) since Sun 2023-03-05 09:09:25 UTC; 5s ago

  Process: 549 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)

  Process: 545 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)

  Process: 544 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)

 Main PID: 552 (nginx)

   CGroup: /system.slice/nginx.service

        ├─552 nginx: master process /usr/sbin/nginx

        └─553 nginx: worker process

Now nginx has been successfully installed.

The root folder for the NGINX web server is the /usr/share/nginx/html folder while in the case of Apache server it was /var/www/html. In case, you want, you can create the directory using:

$ sudo mkdir -p /var/www/html/wordpress/example.com

When you visit the Public IP of your ec2 instance, you will see the NGINX or Amazon Linux 2 page. If you SFTP connect to your instance, you will find that there are already some items in the root folder including an index.html file which contains the same message as the one being shown on your IP.

You can remove these files in the root folder and upload your own html website or add an index.html page like the following.

Open the file using:

 $ sudo nano /usr/share/nginx/html/index.html

and edit it a bit. For example, you can change the heading to welcome to my website. Once done save it using ctrl + X, y and enter.

Now, reload nginx using:

$ sudo systemctl reload nginx

If you visit your public ip, you will see the message has changed.  If you want the root to be /var/www/html/wordpress/website.com, create the directory using mkdir command and add the index.html to it and then add it as the root in the nginx configuration file for your website.

Install SSL on your NGINX Website:

Now, we have to apply the ssl. Before, we can apply ssl on our nginx website, we will need to change the dns records and add a configuration file for our website.

Change the A records and point your domain to the public ip of your instance.

Then create a configuration file for the website you are going to create.

$ sudo nano /etc/nginx/conf.d/example.com.conf

Add the following to this file : –

server

    {    listen *:80;      

 server_name example.com www.example.com;      

 root /usr/share/nginx/html;

#(root /var/www/html/wordpress/domain-name)       

  location / {         

   index index.html index.php  

   }

}

 Save the file and exit. Now, we are ready to install ssl certificate on our website. Replace example.com with your domain name in the above file so you can get ssl certificates for both www and non www versions.

Now, we will first install certbot for nginx and then apply ssl.

To install certbot on nginx, use the following command:

$ sudo yum update -y
$ sudo yum install certbot
$ sudo yum install python-certbot-nginx   

After certbot installation is complete, test nginx syntax using nginx -t and reload nginx using:-

$ sudo systemctl reload nginx

Now, the final step, which is to obtain the ssl certificate for our website.

Run the following command to obtain the certificates:

$ sudo certbot --nginx -d example.com -d www.example.com

Enter your email when prompted to and then agree to the terms of service by replying yes.

Following that, your account will be registered and the certbot will check if the server is listening on port 80 and which websites are listed in the server block. This will be the output before the ssl certificates are installed: –

Account registered.

Requesting a certificate for example.com and www.example.com

Performing the following challenges:

http-01 challenge for example.com

http-01 challenge for www.example.com

Waiting for verification…

Cleaning up challenges

Deploying Certificate to VirtualHost /etc/nginx/conf.d/example.com.conf

Deploying Certificate to VirtualHost /etc/nginx/conf.d/example.com.conf

Redirecting all traffic on port 80 to ssl in /etc/nginx/conf.d/example.com.conf

Redirecting all traffic on port 80 to ssl in /etc/nginx/conf.d/example.com.conf

At the end, you will receive the congratulations message that you have successfully enabled ssl for both versions of your site and the location where your ssl certificate and chain have been saved.

IMPORTANT NOTES:

 – Congratulations! Your certificate and chain have been saved at:

   /etc/letsencrypt/live/example.com/fullchain.pem

   Your key file has been saved at:

   /etc/letsencrypt/live/example.com/privkey.pem

   Your certificate will expire on 2023-xx-xx. To obtain a new or

   tweaked version of this certificate in the future, simply run

   certbot again with the “certonly” option. To non-interactively

   renew *all* of your certificates, run “certbot renew”

That’s all. You have successfully installed nginx and enabled ssl for your website on nginx webserver.