Install EC2 Linux2023 and HTTPD or NGINX

This should be okay for either ARM or x86. Some say ARM is slower, but that may apply to other services rather than AWS.

We can install on t3a instances for x86, or t4g on ARM. Always use GP3 disk.

I prefer NGINX, but httpd is a good starting place or can help with software packages that have issues with nginx.

My example uses Sydney – ap-southeast-2 and subnet ap-southeast-2a. I use GP3 hard disk. Make sure Credit Specification is Standard.
Please subscribe to Linux2023 in the Market Place at $0 cost.

I use laurenceshaw.au in the examples. All my Linux commands are via the vi editor. My SSH key is a .pem file on an iMac logging into Linux as root.

Please be reasonably familiar with pre-requisites such as iMac root terminal login, Connecting to the EC2 instance, sudo su once logged in, and use of FileZilla with your .pem key (.ppk on Windows Putty)

Launch an Instance - Configurations

Please modify the following for Linux2023, rather than Debian as shown.

If you find an instance is taking forever to do things, such as a ssh login, or setting swap space, please dump it and start another one.

Linux2023 configurations (including httpd)

On an iMac terminal, previously set up your ability to use root login for the ssh command to work. (Internet search to see how, or view previous articles). These small shell scripts can be helpful when you log into a terminal session and change to root with “sudo su”.\

If installing Nginx, skip the configs for httpd below, and when completed, go to the toggle section on Nginx.

vi ssh.sh

#!/bin/sh
:>/var/root/.ssh/known_hosts
exit
[save and exit]

chmod 777 ssh.sh

[use your own .pem key file name and location, and the ssh command from the EC2 Connect tab.]
vi domain.sh

#!/bin/sh
cd PEM
ssh -i "domain.pem" admin@ec2-xx-xx-xx-xx.ap-southeast-2.compute.amazonaws.com
exit
[save and exit]

chmod 777 domain.sh

These can assist for quick logins and clearing the ssh keys on your iMac. For example: ./domain.sh

Create swap space before updating the OS:

echo "vm.swappiness=10" >> /etc/sysctl.conf
echo "vm.vfs_cache_pressure=200" >> /etc/sysctl.conf
sysctl -w vm.swappiness=10
sysctl -w vm.vfs_cache_pressure=200
dd if=/dev/zero of=/swapfile bs=1024 count=1048576
mkswap /swapfile
chmod 0600 /swapfile
swapon /swapfile
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
free -m

 

Change /etc/bashrc. I use this: (replace laurenceshaw.au with your domain)

vi /etc/bashrc
# [ "$PS1" = "\\s-\\v\\\$ " ] && PS1="[\u@\h \W]\\$ "
  [ "$PS1" = "\\s-\\v\\\$ " ] && PS1="[\u@laurenceshaw.au: \w]\\$ "

[save and exit]

vi /etc/selinux/config

# SELINUX=permissive
SELINUX=disabled

[save and exit]

dnf update

dnf check-release-update

for example: dnf upgrade --releasever=2023.7.20250512
THIS CAN TAKE A FOREVER ON NANO instances. It is ridiculous that an upgrade on a new instance can take so long. When the instance first comes publicly available, there will be missing packages as well.

dnf install php8.3

If this does not work, do dnf install php, then dnf list|grep php should then show 8.3 and 8.4 are available/
That being the case, then dnf remove php; dnf install php8.3

php -v  

This should include php-common. 

dnf install -y httpd httpd-tools mod_ssl httpd-devel

If /etc/httpd/conf.modules.d has these files: 10-h2.conf and 10-proxy_h2.conf, they will not work, so move them to 10-h2.conf.o and 10-proxy_h2.conf.o. This is because on a small instance we are not using http2 due to resource loads. Nginx would not have this http2 issue.

dnf install php-pear 

Check dnf wget is already installed, if not, do so.

dnf install php-mysqli php-mbstring
dnf install -y php-cli php-pdo php-fpm php-json php-mysqlnd php-opcache
dnf install -y gd libzip-devel kernel-devel php-gd
dnf install -y cronie cronie-anacron
dnf -y install pcre-devel gcc zlib zlib-devel
dnf -y install mariadb105
dnf -y install mariadb105-server
dnf -y install libjpeg-turbo-utils

pecl install zip
pecl channel-update pecl.php.net

Use your own values... (some people prefer 256M)
vi /etc/php.ini

;;;;;;;;;;;;;;;;;;;;;;
; Dynamic Extensions ;
;;;;;;;;;;;;;;;;;;;;;;
extension=zip.so;
date.timezone = Australia/Brisbane
max_execution_time = 300
max_input_time = 600
max_input_vars = 2500
post_max_size = 100M
upload_max_filesize = 100M
max_file_uploads = 20
memory_limit = 512M

[save and exit]

If you get error log warnings that the extension is already installed, just remove the extension line above.
Note: the semicolon is used to comment out php.ini lines. 

vi /etc/php.d/10-opcache.ini
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=4000

[save and exit]

If your error logs show a warning that zip is already loaded, just remove the extension line above.

cd /etc/php-fpm.d

cp -p www.conf www.conf.o

vi /etc/php-fpm.d/www.conf

pm = ondemand
pm.max_children = 75
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.process_idle_timeout = 10s;
pm.max_requests = 500
php_admin_value[disable_functions] = exec,passthru,system
php_admin_flag[allow_url_fopen] = off
php_admin_value[memory_limit] = 512M

[save and exit. Note the timeout line has a semicolon at the end]

I also edit /etc/php-fpm.conf emergency settings in case processes fail and need an auto restart, with a graceful reload of php-fpm.

vi /etc/php-fpm.conf

emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 60s

[save and exit]

We then use crontab to reload php-fpm once a night, here shown as 5 past midnight, due to memory leaks and disk swap space not cleared out - reload, not restart"

crontab -e
5 0 * * * /usr/bin/systemctl reload php-fpm >/dev/null 2>&1

[save and exit]

If using Let's Encrypt:

dnf -y install python3 python3-devel augeas-libs
python3 -m venv /opt/certbot/
/opt/certbot/bin/pip install --upgrade pip
/opt/certbot/bin/pip install certbot 

Note: I do not install certbot-apache
ln -s /opt/certbot/bin/certbot /usr/bin/certbot

Mariadb and phpMyAdmin

systemctl start mariadb
mysql_secure_installation
["Enter current password for root" (enter for none): 
OK, successfully used password, moving on...
"Switch to unix_socket authentication [Y/n]"  n
"Change the root password?" [Y/n] Y
(nominate your database password)
Y for the remaining questions]
[Note that we now start and enable all our services. If httpd is not loading SSL correctly, you need to problem solve.]
systemctl stop mariadb
systemctl start mariadb
systemctl enable mariadb

Note: we are assuming httpd instead of Nginx for this example.

systemctl enable httpd            

systemctl enable php-fpm


Configuring phpMyAdmim:

cd /usr/share

wget https://www.phpmyadmin.net/downloads/phpMyAdmin-latest-all-languages.tar.gz
ls
tar xvf .....  

[where ..... is the downloaded file.
Then delete the tar.gz file, then use the Unix command to move the directory to phpMyAdmin, e.g.: mv yourfile phpMyAdmin]

cd phpMyAdmin
mkdir tmp
chmod 777 tmp
cp -p config.sample.inc.php config.inc.php
vi config.inc.php

[
Search for the blowfish line. Do a Google search on blowfish phpmyadmin generator.
I use: https://phpsolved.com/phpmyadmin-blowfish-secret-generator/?g=[insert_php]echo%20$code;[/insert_php] from https://phpsolved.com.
Paste the generated value into the blowfish value.
Then after SaveDir as shown below, add TEMPDir...
]

$cfg['SaveDir'] = '';
$cfg['TempDir'] = '/tmp';

We do not start httpd until further configurations below.

We do not have SSL running, so you should not log into phpMyAdmin until then.

http://mydomain.com/phpMyAdmin is the way we access it. We will install a phpMyAdmin.conf file to do this.


We need to edit some /etc/httpd/conf.modules.d files:

Edit /etc/httpd/conf/httpd.conf as follows:

Use your own primary domain name assuming /var/www/html
Require all granted is to allow WordPress to use permalinks to the web's page name.

cd /etc/httpd/conf

vi httpd.conf

# After the Listen:80 line, add this:
KeepAlive On
MaxKeepAliveRequests 50
KeepAliveTimeout 5

ServerName YOUR_DOMAIN

DocumentRoot "/var/www/html"
<Directory "/var/www">
    AllowOverride None
    # Allow open access:
    Require all granted
</Directory>
<Directory "/var/www/html">
 Options Indexes FollowSymLinks
 AllowOverride All
 Require all granted
</Directory>
<IfModule dir_module>
    DirectoryIndex index.php index.html
</IfModule>

[save and exit]

We now add SSL - lets first assume we are using a paid certificate. Use your own names: (assuming /var/www/html)

For the first edit of ServerName, locate that entry and edit from there.
Don't edit RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent] as the CPITAL letters are correct as they are.

Be careful not to have duplicate entries. I have added the new lines with indents.
You cannot start httpd immediately after this edit. We have to modify more files, however, makre sure your SSL certificate is uploaded via FIleZilla, and transferred to /etc/pki/tls/... as in the example below.

cd /etc/httpd/conf.d

vi ssl.conf

At the top add:

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

Under the section header <VirtualHost _default_:443> add the following. No duplicates and use your own domain and SSL.

   ServerName laurenceshaw.au:443
   DocumentRoot /var/www/html
   Redirect permanent / https://laurenceshaw.au/
   RewriteEngine on
   RewriteCond %{SERVER_NAME} =laurenceshaw.au
   RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

# Then, add: (withourt duplicates) under SSL Engine on (check it is on):


   SSLProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2
   SSLProxyProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2
   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
   SSLProxyCipherSuite 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:AES:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA

# These should be present, so under them add the indexed lines I have shown:

SSLHonorCipherOrder on
SSLCipherSuite PROFILE=SYSTEM
SSLProxyCipherSuite PROFILE=SYSTEM
   SSLCompression off
   SSLInsecureRenegotiation Off
   SSLSessionTickets Off
   SSLOpenSSLConfCmd ECDHParameters secp384r1
   SSLOpenSSLConfCmd Curves secp384r1
   SSLCertificateFile /etc/pki/tls/certs/laurenceshaw_au.crt
   SSLCertificateKeyFile /etc/pki/tls/private/laurenceshaw_au.key

# at the end of the file you will see </VirtualHost> to close off the 443 section.

[save and exit]

vi /etc/httpd/conf.modules.d/00-mpm.conf

[We do not use http/2 on our smaller instances]

LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
#LoadModule mpm_worker_module modules/mod_mpm_worker.so

[append the following lines for performance:]

StartServers 2
MinSpareServers 2
MaxSpareServers 5
MaxRequestWorkers 125
ServerLimit 125
MaxConnectionsPerChild 0

[save and exit]

[comment out "heartbeat" to stop constant error logging:]

vi /etc/httpd/conf.modules.d/00-proxy.conf

# LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so

[save and exit]

A generic phpMyAdmin file:

cd /etc/httpd/conf.d

vi phpMyAdmin.conf

Alias /phpMyAdmin /usr/share/phpMyAdmin
Alias /phpmyadmin /usr/share/phpMyAdmin
<Directory /usr/share/phpMyAdmin/>
   AddDefaultCharset UTF-8
   Require local
   Require all granted
</Directory>
<Directory /usr/share/phpMyAdmin/setup/>
   Require local
   Require all granted
</Directory>
<Directory /usr/share/phpMyAdmin/setup/frames/>
    Order Deny,Allow
    Deny from All
    Allow from None
</Directory>

[save and exit]





For httpd, use these configurations, which will mention the difference for LetsEncrypt as well.

These configs also show multi-domains on the same server. Notice how the redirect lines only apply to the port 80 stanzas.

Here is an example of multi-domain ssl.conf file: notice the second VritualHost section for snotbat.com, and the second 443 section.
We assume paid SSL.

IF YOU WANT LETSENCRYPT please see my other articles, then have a version of this ssl.conf file without the 443 section(s) and comment out the "Redirect permanent" lines.
Then use the --dry-run option (as per my articles) and if okay, install it, then put back the version of ssl.conf that has all the lines and no comment on Redirect.
It should be that straight forward, but do see my articles on Debian as to how this works. Debian SSL configs are different.

<VirtualHost *:80>
ServerName laurenceshaw.au
Redirect permanent / https://laurenceshaw.au/
RewriteEngine on
RewriteCond %{SERVER_NAME} =laurenceshaw.au
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
<VirtualHost *:80>
ServerName snotbat.com
Redirect permanent / https://snotbat.com/
RewriteEngine on
RewriteCond %{SERVER_NAME} =snotbat.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
#
# When we also provide SSL we have to listen to the 
# standard HTTPS port in addition.
#
Listen 443 https
##
##  SSL Global Context
##
##  All SSL configuration in this context applies both to
##  the main server and all SSL-enabled virtual hosts.
##
#   Pass Phrase Dialog:
#   Configure the pass phrase gathering process.
#   The filtering dialog program (`builtin' is a internal
#   terminal dialog) has to provide the pass phrase on stdout.
SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog
#   Inter-Process Session Cache:
#   Configure the SSL Session Cache: First the mechanism 
#   to use and second the expiring timeout (in seconds).
SSLSessionCache         shmcb:/run/httpd/sslcache(512000)
SSLSessionCacheTimeout  300
#   Pseudo Random Number Generator (PRNG):
#   Configure one or more sources to seed the PRNG of the 
#   SSL library. The seed data should be of good random quality.
#   WARNING! On some platforms /dev/random blocks if not enough entropy
#   is available. This means you then cannot use the /dev/random device
#   because it would lead to very long connection times (as long as
#   it requires to make more entropy available). But usually those
#   platforms additionally provide a /dev/urandom device which doesn't
#   block. So, if available, use this one instead. Read the mod_ssl User
#   Manual for more details.
SSLRandomSeed startup file:/dev/urandom  256
SSLRandomSeed connect builtin
#SSLRandomSeed startup file:/dev/random  512
#SSLRandomSeed connect file:/dev/random  512
#SSLRandomSeed connect file:/dev/urandom 512
#
# Use "SSLCryptoDevice" to enable any supported hardware
# accelerators. Use "openssl engine -v" to list supported
# engine names.  NOTE: If you enable an accelerator and the
# server does not start, consult the error logs and ensure
# your accelerator is functioning properly. 
#
SSLCryptoDevice builtin
#SSLCryptoDevice ubsec
##
## SSL Virtual Host Context
##
<VirtualHost _default_:443>
# General setup for the virtual host, inherited from global configuration
#DocumentRoot "/var/www/html"
#ServerName www.example.com:443
ServerName laurenceshaw.au:443
# Use separate log files for the SSL virtual host; note that LogLevel
# is not inherited from httpd.conf.
ErrorLog logs/ssl_error_log
TransferLog logs/ssl_access_log
LogLevel warn
#   SSL Engine Switch:
#   Enable/Disable SSL for this virtual host.
SSLEngine on
#   List the protocol versions which clients are allowed to connect with.
#   The OpenSSL system profile is configured by default.  See
#   update-crypto-policies(8) for more details.
#SSLProtocol all -SSLv3
#SSLProxyProtocol all -SSLv3
SSLProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2
SSLProxyProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2
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
SSLProxyCipherSuite 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:AES:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
#   User agents such as web browsers are not configured for the user's
#   own preference of either security or performance, therefore this
#   must be the prerogative of the web server administrator who manages
#   cpu load versus confidentiality, so enforce the server's cipher order.
SSLHonorCipherOrder on
#   SSL Cipher Suite:
#   List the ciphers that the client is permitted to negotiate.
#   See the mod_ssl documentation for a complete list.
#   The OpenSSL system profile is configured by default.  See
#   update-crypto-policies(8) for more details.
SSLCipherSuite PROFILE=SYSTEM
SSLProxyCipherSuite PROFILE=SYSTEM
SSLCompression off
SSLInsecureRenegotiation Off
SSLSessionTickets Off
SSLOpenSSLConfCmd ECDHParameters secp384r1
SSLOpenSSLConfCmd Curves secp384r1
#   Point SSLCertificateFile at a PEM encoded certificate.  If
#   the certificate is encrypted, then you will be prompted for a
#   pass phrase.  Note that restarting httpd will prompt again.  Keep
#   in mind that if you have both an RSA and a DSA certificate you
#   can configure both in parallel (to also allow the use of DSA
#   ciphers, etc.)
#   Some ECC cipher suites (http://www.ietf.org/rfc/rfc4492.txt)
#   require an ECC certificate which can also be configured in
#   parallel.
SSLCertificateFile /etc/pki/tls/certs/laurenceshaw_au.crt
#   Server Private Key:
#   If the key is not combined with the certificate, use this
#   directive to point at the key file.  Keep in mind that if
#   you've both a RSA and a DSA private key you can configure
#   both in parallel (to also allow the use of DSA ciphers, etc.)
#   ECC keys, when in use, can also be configured in parallel
SSLCertificateKeyFile /etc/pki/tls/private/laurenceshaw_au.key
#   Server Certificate Chain:
#   Point SSLCertificateChainFile at a file containing the
#   concatenation of PEM encoded CA certificates which form the
#   certificate chain for the server certificate. Alternatively
#   the referenced file can be the same as SSLCertificateFile
#   when the CA certificates are directly appended to the server
#   certificate for convenience.
#SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt
#   Certificate Authority (CA):
#   Set the CA certificate verification path where to find CA
#   certificates for client authentication or alternatively one
#   huge file containing all of them (file must be PEM encoded)
#SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt
#   Client Authentication (Type):
#   Client certificate verification type and depth.  Types are
#   none, optional, require and optional_no_ca.  Depth is a
#   number which specifies how deeply to verify the certificate
#   issuer chain before deciding the certificate is not valid.
#SSLVerifyClient require
#SSLVerifyDepth  10
#   Access Control:
#   With SSLRequire you can do per-directory access control based
#   on arbitrary complex boolean expressions containing server
#   variable checks and other lookup directives.  The syntax is a
#   mixture between C and Perl.  See the mod_ssl documentation
#   for more details.
#<Location />
#SSLRequire (    %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \
#            and %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd." \
#            and %{SSL_CLIENT_S_DN_OU} in {"Staff", "CA", "Dev"} \
#            and %{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5 \
#            and %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20       ) \
#           or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/
#</Location>
#   SSL Engine Options:
#   Set various options for the SSL engine.
#   o FakeBasicAuth:
#     Translate the client X.509 into a Basic Authorisation.  This means that
#     the standard Auth/DBMAuth methods can be used for access control.  The
#     user name is the `one line' version of the client's X.509 certificate.
#     Note that no password is obtained from the user. Every entry in the user
#     file needs this password: `xxj31ZMTZzkVA'.
#   o ExportCertData:
#     This exports two additional environment variables: SSL_CLIENT_CERT and
#     SSL_SERVER_CERT. These contain the PEM-encoded certificates of the
#     server (always existing) and the client (only existing when client
#     authentication is used). This can be used to import the certificates
#     into CGI scripts.
#   o StdEnvVars:
#     This exports the standard SSL/TLS related `SSL_*' environment variables.
#     Per default this exportation is switched off for performance reasons,
#     because the extraction step is an expensive operation and is usually
#     useless for serving static content. So one usually enables the
#     exportation for CGI and SSI requests only.
#   o StrictRequire:
#     This denies access when "SSLRequireSSL" or "SSLRequire" applied even
#     under a "Satisfy any" situation, i.e. when it applies access is denied
#     and no other module can change it.
#   o OptRenegotiate:
#     This enables optimized SSL connection renegotiation handling when SSL
#     directives are used in per-directory context. 
#SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
<FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/var/www/cgi-bin">
    SSLOptions +StdEnvVars
</Directory>
#   SSL Protocol Adjustments:
#   The safe and default but still SSL/TLS standard compliant shutdown
#   approach is that mod_ssl sends the close notify alert but doesn't wait for
#   the close notify alert from client. When you need a different shutdown
#   approach you can use one of the following variables:
#   o ssl-unclean-shutdown:
#     This forces an unclean shutdown when the connection is closed, i.e. no
#     SSL close notify alert is sent or allowed to be received.  This violates
#     the SSL/TLS standard but is needed for some brain-dead browsers. Use
#     this when you receive I/O errors because of the standard approach where
#     mod_ssl sends the close notify alert.
#   o ssl-accurate-shutdown:
#     This forces an accurate shutdown when the connection is closed, i.e. a
#     SSL close notify alert is sent and mod_ssl waits for the close notify
#     alert of the client. This is 100% SSL/TLS standard compliant, but in
#     practice often causes hanging connections with brain-dead browsers. Use
#     this only for browsers where you know that their SSL implementation
#     works correctly. 
#   Notice: Most problems of broken clients are also related to the HTTP
#   keep-alive facility, so you usually additionally want to disable
#   keep-alive for those clients, too. Use variable "nokeepalive" for this.
#   Similarly, one has to force some clients to use HTTP/1.0 to workaround
#   their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and
#   "force-response-1.0" for this.
BrowserMatch "MSIE [2-5]" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0
#   Per-Server Logging:
#   The home of a custom SSL log file. Use this when you want a
#   compact non-error SSL logfile on a virtual host basis.
CustomLog logs/ssl_request_log \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</VirtualHost>
<VirtualHost *:443>
 ServerName snotbat.com:443
 DocumentRoot /var/www/snotbat.com
<Directory /var/www/snotbat.com>
Options None FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog logs/ssl_error_log
TransferLog logs/snotbat.com_ssl_access_log
LogLevel warn
 SSLEngine on
SSLProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2
SSLProxyProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2
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
SSLProxyCipherSuite 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:AES:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
SSLHonorCipherOrder on
SSLCipherSuite PROFILE=SYSTEM
SSLProxyCipherSuite PROFILE=SYSTEM
SSLCompression off
SSLInsecureRenegotiation Off
SSLSessionTickets Off
SSLOpenSSLConfCmd ECDHParameters secp384r1
SSLOpenSSLConfCmd Curves secp384r1
 SSLCertificateFile /etc/pki/tls/certs/snotbat_com.crt
 SSLCertificateKeyFile /etc/pki/tls/private/snotbat_com.key
<FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/var/www/cgi-bin">
    SSLOptions +StdEnvVars
</Directory>
BrowserMatch "MSIE [2-5]" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0
CustomLog logs/snotbat.com_ssl_request_log \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</VirtualHost>


Please see my other articles on phpMyAdmin and the basics of LetsEncrypt/Certbot.

The key thing with certbot is to only have port 80’s server domain without redirection to https. After the –dry-run is successful, install, then uncomment the https redirect in th eport 80 stanza, and add the port 443 stanza back with the certificate location correctly specified.

Please see other articles for AWS software and WordPress configurations.


Linux 2023 Nginx (stable 1.27 or above) Installation


Linux 2023 Nginx with memcached

Please verify your complete configurations are the same as the toggle sections above, excepting httpd of course. I have repeated some content, but not all, and have shown where settings differ to apache user/group versus apache/apache.

You will see all the nginx file configurations as per other articles using nginx.

I know the content is therefore a bit mixed, but I didn’t wish to spend time clarifying further.

We really do not want less the nginx v1.27. Earlier versions make use of sites-available/enabled and its various config files that can have their content in the main nginx.conf file instead, and specifies http2 differently. If you need typically v1.22 due to other apps, then go for it.

Wen installing packages, do not include httpd. However, you can use dnf remove *httpd* if desired, and make sure httpd is not enabled or running.
We will need to modify some configureations to use nginx file ownerships instead of apache.

Assuming php8.3 below. We need memcached for nginx to work best.
Check dnf check-release-update before and after memcached is installed. (We never install memcache, but rather memcached, as that is a different product)

cd /home/ec2-user

dnf -y install php8.3-devel php-pear gcc && \
   pear update channels && \
   pecl update channels && \
   \
   # build + install igbinary for use by php-memcached
   
   pecl install igbinary && \
   echo "extension=igbinary.so" | sudo tee /etc/php.d/20-igbinary.ini && \
   \
   # build + install msgpack for use by php-memcached
   pecl install msgpack && \
   echo "extension=msgpack.so" | sudo tee /etc/php.d/20-msgpack.ini && \
   \
   # build + install php-memcached
   dnf install -q -y memcached-devel memcached \
       libmemcached-awesome-devel libmemcached-awesome \
       zlib-devel zlib cyrus-sasl cyrus-sasl-devel \
       libevent libevent-devel && \
   pecl install --configureoptions \
      'with-zlib-dir="no" \
       with-system-fastlz="no" \
       with-libmemcached-dir="no" \
       enable-memcached-igbinary="yes" \
       enable-memcached-msgpack="yes" \
       enable-memcached-json="yes" \
       enable-memcached-protocol="yes" \
       enable-memcached-sasl="yes" \
       enable-memcached-session="yes"' memcached &&  \
   echo "extension=memcached.so" | sudo tee /etc/php.d/25-memcached.ini && \
   \
   # clean up php dev tools and the dnf cache
   dnf remove -y gcc php8.3-devel php-pear libzip-devel \
      memcached-devel libmemcached-awesome-devel zlib-devel \
      cyrus-sasl-devel libevent-devel && \
   dnf autoremove -y && dnf clean all && rm -rf /var/cache/dnf


Now run:


pear update-channels
pecl update-channels

Now run these commands to fix the errors:


dnf install -q -y memcached-devel libmemcached-awesome-devel zlib-devel cyrus-sasl-devel libevent-devel
/usr/bin/yes 'no' | pecl install --configureoptions 'enable-memcached-igbinary="yes" enable-memcached-msgpack="yes" enable-memcached-json="yes" enable-memcached-protocol="yes" enable-memcached-sasl="yes" enable-memcached-session="yes"' memcached

echo 'extension=memcached.so' > /etc/php.d/41-memcached.ini

Now check /etc/php.d as the 41-memcached.file. You can see where these scripts fail, and modify which lines to run.

cd /
find . -name memcached.so -print
./usr/lib64/php8.3/modules/memcached.so

Without this file, phpMyAdmin will not work.

systemctl enable memcached

Just repeating the dnf packages which should be the same as httpd installation above:

dnf install -y php-pear wget php-mysqli php-devel php-mbstring
dnf install -y php-cli php-pdo php-fpm php-json php-mysqlnd php-opcache
dnf install -y gd libzip-devel kernel-devel php-gd
dnf install -y cronie cronie-anacron
dnf -y install pcre-devel gcc zlib zlib-devel
dnf -y install mariadb105
dnf -y install mariadb105-server
dnf -y install libjpeg-turbo-utils

I do not do dnf remove *apache* as there are dependencies.

I also use w3 Total Cache plugin to make use of memcached in WordPress.

I had an error when I first tried to install zip. We need it. If an error, just check no error message on a missing package, and if it appears missing but is present, reinstall with dnf reinstall, and check the channel-update is done.

Zip should have been okay in the httpd installation notes above, but repeating it here:

pecl install zip
pecl channel-update pecl.php.net

Check the extension exists:

find . -name zip.so -print
./usr/lib64/php8.3/modules/zip.so

cd /etc/php-fpm.d
cp -p www.conf www.conf.o

Some of the information below is a little different to the httpd installation, so best to wade throught it even if repeated configs are shown.
You will see the memcached configurations for www.conf and php.ini. You must have the EC2 Security port opened with TCP:

Custom TCP TCP 11211 127.0.0.0/16 memcached

INSTALL Current Stable version of Nginx:

dnf install yum-utils

In the vi editing below, we must include the lines that have [....] in them.


SEE: https://nginx.org/en/linux_packages.html#Amazon-Linux

vi /etc/yum.repos.d/nginx.repo

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/amzn/2023/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
priority=9

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/amzn/2023/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
priority=9

[save and exit]

yum-config-manager --enable nginx-mainline

I think the next line fails, but I used it anyway.

dnf install nginx.  ---> it prompt for the Enter key a few times. 

Just check these commands have been done somewhere along the way, just in case:

dnf install libgd
dnf install lib-gd
dnf install gd

nginx -t

We now need to configure nginx before using it. See my notes on nginx to do this.

Also:

When we start nginx, if you get the error: (you should be ok)

systemctl status -l nginx
nginx.service: Can't open PID file /run/nginx

Our reference for the fix is: https://serverfault.com/questions/1042526/open-run-nginx-pid-failed-13-permission-denied

mkdir -p /etc/systemd/system/nginx.service.d

Create these lines:

vi /etc/systemd/system/nginx.service.d/override.conf

[Service]
ExecStartPost=/bin/sleep 0.1

[save and exit]

systemctl daemon-reload
systemctl restart nginx
systemctl enable nginx
systemctl status -l nginx ------ check the top lines for the /run/nginx.pid or /var/run/nginx.pid if you get an error in the status

Do a ps -ef to see nginx is running and nginx -v, and nginx -t. /etc/nginx.conf configurations are as per the Debian article.

vi /etc/php-fpm.d/www.conf

user = nginx
group = nginx 

pm = ondemand
pm.max_children = 75
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.process_idle_timeout = 10s;
pm.max_requests = 500
php_admin_value[disable_functions] = exec,passthru,system
php_admin_flag[allow_url_fopen] = off
php_admin_value[memory_limit] = 512M
php_value[opcache.file_cache]  = /var/lib/php/opcache

Note: I have not uncommented opcache before, but it makes sense to do so.

If using memcached, fix as follows:

; php_value[session.save_handler] = files
; php_value[session.save_path]    = /var/lib/php/session
php_value[session.save_handler] = memcached
php_value[session.save_path]    = 127.0.0.1:11211

[save and exit. Note the timeout line has a semicolon at the end]

*** We do not add a memory or autosave value in WordPress wp-config.php when using Nginx *** Please check.

We add the next three lines to php-fpm.conf so we can use systemctl reload php-fpm, rather than restart, and for memory leaks - according to Internet forums

vi /etc/php-fpm.conf

emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 60s

[save and exit]

We use crontab to safely reload php-fpm once a night, here shown at 5 past midnight. Reload will not impact visitors.

crontab -e

5 0 * * * /usr/bin/systemctl reload php-fpm >/dev/null 2>&1

[save and exit]

If using Let's Encrypt:

dnf -y install python3 python3-devel augeas-libs
python3 -m venv /opt/certbot/
/opt/certbot/bin/pip install --upgrade pip
/opt/certbot/bin/pip install certbot
ln -s /opt/certbot/bin/certbot /usr/bin/certbot

Install Mariadb:

systemctl start mariadb
mysql_secure_installation

"Enter current password for root" (enter for none): 
OK, successfully used password, moving on...
"Switch to unix_socket authentication [Y/n]"  n
"Change the root password?" [Y/n] Y
(nominate your database password)
Y for the remaining questions]

We now start and enable our mariadb services. Enabling means they start at a reboot.
If any problems, stop and restart your EC2 instance or do "sync;sync;reboot"

systemctl stop mariadb
systemctl start mariadb
systemctl enable mariadb

There is a problem in the current Linux2023 as at writing, where systemctl status -l mariadb says: Database MariaDB is probably initialized in /var/lib/mysql already, nothing is done.
Just ignore this as ps -ef|grep mariadb shows the database is fine. I have not found how to correct this as yet.

systemctl enable php-fpm
systemctl start php-fpm

Install phpMyAdmin the same way as in my othr articles:

cd /usr/share

wget https://www.phpmyadmin.net/downloads/phpMyAdmin-latest-all-languages.tar.gz
ls
tar xvf phpMyAdmin-latest-all-languages.tar.gz
rm phpMyAdmin-latest-all-languages.tar.gz
mv phpMyAdmin-5.2.1-all-languages phpMyAdmin  
cd phpMyAdmin
mkdir tmp
chmod 777 tmp
cp -p config.sample.inc.php config.inc.php
vi config.inc.php

[Search for the blowfish line. Do a Google search on blowfish phpmyadmin generator.
Avoid slashes and exlamation marks in the generator's output, so yu get used to avoiding these in general IT usage.
I use: https://phpsolved.com/phpmyadmin-blowfish-secret-generator/?g=[insert_php]echo%20$code;[/insert_php] from https://phpsolved.com.
Paste the generated value into the blowfish value.

Then after SaveDir as shown below, add TempDir...

$cfg['SaveDir'] = '';
$cfg['TempDir'] = '/tmp';

[save and exit]

Permissions need to be set for nginx:

cd /usr/share
chgrp nginx phpMyAdmin
chmod 775 phpMyAdmin
ls -l | grep phpMyAdmin
drwxrwxr-x.  13 root nginx 16384 Oct  6 19:48 phpMyAdmin

cd phpMyAdmin
chown nginx index.php
chmod 664 index.php
ls -l
-rw-r--r--.  1 nginx root   1074 Feb  8  2023 index.php

Make sure your domain name DNS records are correct and the A record uses the instance IP4 address. As we are not configuring IP6 on the instance, we do not use an AAAA record. If we did ahve IP6, you would need an AAAA record as well.

Here is an example of how you link to phpMyAdmin from the browser. Due to security, DO NOT log into phpMyAdmin until after we install Nginx and https:// is working. 

cd /var/www/html
pwd
ln -s /usr/share/phpMyAdmin phpMyAdmin
ls -l

Note: you never want to change the ownerships and permissions of soft links. Be careful of this when changing permissions of WordPress files in the same directory.

To get phpmyadmin to work have nginx group as shown: (remove apache or www-data if seen on these files as shown here)

cd /var/lib/php

ls -l
total 0
drwxrwx---. 2 root nginx 6 Feb 13 06:23 opcache
drwxr-xr-x. 2 root root   6 Feb 13 06:23 peclxml
drwxrwx---. 2 root nginx 6 Feb 13 06:23 session
drwxrwx---. 2 root nginx 6 Feb 13 06:23 wsdlcache

fyi: later you can create databases in phpMyAdmin with utf8mb4_general_ci if you like

Check permissions and ownershios for the default /var/www/html directories we will use with Nginx. The same applies to multi-domains under, say, /var/www.

cd /var
ls -l

Set these permissions: drwxrwsr-x 12 root nginx 4096 Aug  2 00:00 www
e.g. chown root www;chgrp nginx www;chmod 2775 www

cd /var/www
ls -l

If html is not present, create it with "mkdir html"
Set these permissions: drwxrwsr-x  3 nginx nginx  4096 Jul 17 16:30 html
e.g. chown nginx html;chgrp nginx html;chmod 2775 html

Add the phpinfo.php file which we will use after Nginx is configured, to verify php configurations, including memcached if installed:

echo " < ?phpZphpinfo(); ? > "|sed 's/ //g'|sed 's/Z/ /g' > phpinfo.php
chown nginx p*; chgrp nginx p*; chmod 664 p*
ls -l

When we do install nginx, there is a process we go through to add SSL certificates. To test https://mydomain.com, you would use https://mydomain.com/phhpinfo.php, or add a dummy index.html file:

cd /var/www/html
vi index.html
testing mydomain.com

[save and exit]


Make sure your file(s) in /car/www/html are chown nginx *; chgrp nginx *; chmod 664 *

Check memcached is also added to php.ini:

; session.save_handler = files
  session.save_handler = memcached
  session.save_path = "127.0.0.1:11211"

cd /etc/sysconfig

vi memcached

  PORT="11211"
  USER="memcached"
  MAXCONN="1024"
  CACHESIZE="64"
  OPTIONS="-l 127.0.0.1 -U 0,::1"

[save and exit]

systemctl start memcached

ps -ef|grep memcached
memcach+   95464       1  0 04:24 ?        00:00:00 /usr/bin/memcached -p 11211 -u memcached -m 64 -c 1024 -l 127.0.0.1 -U 0,::1

NOTE: We do not have a php-memcached package on Linux2023. We can still make use of it in WordPress with the W3 Total Cache setup page.

You should have opcache configured and anything else as per the previous toggle sections above for Linu2023 and HTTPD, excepting the specific configs to httpd.

Do a system restart:

sync;sync;reboot

Log back in, check php-fpm, nginx, memcached, mariadb, (opcache?) are all enabled and running.

Then check https://MYDOMAIN.COM and https://MYDOMAIN.COM/phpinfo.php (check memcached, opcache, yur php.ini values appear to be ok. For memcached, search for session.save and see two columns showing memcached is on.

The configurations have been tested and work. If yu accidentally install the default nginx, you need to remove it and add the current version. If an issue you can then remove the current, and do a “yum reinstall nginx”.

Start typing and press Enter to search