Amazon Linux2023 Nginx and WordPress Installation

Reference: EC2 Linux 2023 AMI on tg4.micro GP3 Disk

Amazon Linux2023 Nginx and WordPress Installation

This article assumes you know how to install the instance and other matters as shown in my other articles.

Changes: please see these references for current version of nginx, and I am still experimenting with how to use memcached and php-memcached, and the amazon doco can be used in part – still remove *httpd* packages.

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

https://www.cloudwithxavier.com/install-memcached-on-amazon-linux-2023-ami/

https://docs.aws.amazon.com/linux/al2023/ug/hosting-wordpress-aml-2023.html

The following steps will install an EC2 Linux 2023 instance with NGINX. There is much similarity with several steps to other installations I’ve documented on this website.
Please replace entries such as domain.com with your own domain name, your own timezone, IP address (if applicable), your own include files (samples given), and your own root directories which may differ to the ones I have shown such as /var/www/html.
See othrr articles on configuring opcache in /etc/php.d, and phpMyAdmin if needed.
I have shown a subdomain example with Lets Encrypt (certbot). I had to trick certbot’s first installation to proceed by using a primary SSL certificate already paid for in the 443 server stanza, but I’d suggest playing around with it and referring to https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/

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

[Use your own Country/City:]

a="Australia/Brisbane";export a;echo $a
ln -sf /usr/share/zoneinfo/$a /etc/localtime
date

[Change /etc/bashrc. I use this:]

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

[save and exit]

vi /etc/selinux/config

# SELINUX=permissive
SELINUX=disabled

[save and exit]

dnf check-release-update

[We will not install postfix as there are changes from earlier releases I need to work through]

[You will notice, no apache installation files]

dnf install -y php php-common php-pear wget php-mysqli php-devel php-mbstring
dnf install -y php-cli php-pdo php-fpm php-json php-mysqlnd php-opcache
dnf install -y gd libzip-devel 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

[Install zip. When done, we edit the /etc/php.ini file.]

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

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 = 256M

[save and exit]

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]

NOTE: Further below are instructions for installing NGINX on Linux2023. We do not do this method for Debian11.
For Debian we must use the current version of NGINX (stable version).
See https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/
If one accidentally installs on Debian with apt install nginx, you will have to create a new instance.

[If using php-fpm...]

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

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

user = nginx
group = nginx 
listen.owner = nginx
listen.group = nginx
listen.mode = 0660
; listen.acl_users = nginx,nginx ---> these must be or remain commented as shown here
; listen.acl_groups =
; pm = dynamic
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] = 256M

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


[If using 256M in WordPress, we use 256 below. With NGINX we will not put a memory value into the WordPress wp-config.php or an AutoSave value.]

[ I also edit /etc/php-fpm.conf. emergency settings in case processes fail and need an auto restart, and 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 certbot-apache
ln -s /opt/certbot/bin/certbot /usr/bin/certbot

[If creating a backup user, see my other Linux2023 installation notes.]

[Install phpmyadmin as per my other Linux2023 installation notes, but into /var/www/phpmyadmin (or /usr/share, anywhere really)
If your main domain is under /var/www/html, do a softlink to phpmyadmin. You may put multi-domains udner /var/www]

[Note: to get phpmyadmin to work have nginx group as shown:]
cd /var/lib
cd 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


[You can use databases with utf8mb4_general_ci if you like]

[We also in nginx.conf further below, will have changed max uploads via the client_max_body_size 50M; directive. Use your preferred size. ONce everythng is working, or sooner, I recommend a stop and start of the EC2 instance.]

[Install and configure Mariadb as per other documentation.]


[Have your DNS A record for the primary domain and any subdomains pointing to the EC2 instance Static IP4 address. Your records do not need to be on Route53 if you can create CAA records. Let's Encrypt "may" permit absence of CAA records, but I have no further details on this.]


******************** INSTALL NGINX *********************

dnf -y install nginx*

[Note: you could try the basic dnf install nginx without the other packages if you wish to try and test how it goes.] 

[If main domain is in /var/www/html:] echo "" > /var/www/html/phpinfo.php]
[Your directories need these permissions, e.g: drwxrwsr-x. 13 root nginx 16384 Apr 23 10:05 html]
[Your WordPress files will need, for example: -rw-rw-r-- 1 nginx nginx 3501 Apr 20 15:26 wp-config.php]

[Remember that if you install php-fpm, and of course mariadb, systemctl enable them, and start them. Same for nginx.]

The following config will work.
vi /etc/nginx.conf

# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    server_names_hash_bucket_size 64;
    # https://spinupwp.com/hosting-wordpress-yourself-nginx-security-tweaks-woocommerce-caching-auto-server-updates/
    add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
    ssl_session_timeout 10m;
    add_header Content-Security-Policy "default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval';" always;
    add_header X-Xss-Protection "1; mode=block" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "origin-when-cross-origin" always;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    client_max_body_size 50M;
    upstream _php {
     server unix:/run/php-fpm/www.sock;
     }

# Include SUBDOMAINS websites:
# include /etc/nginx/second_domain.com.conf;


    server {
        listen       80;
        listen       [::]:80;
        server_name  domain.com www.domain.com;
        return 301 https://$host$request_uri;
        root         /var/www/html;

 	  # add_header X-Frame-Options "SAMEORIGIN";
    # add_header X-Content-Type-Options "nosniff";
    index index.php index.html index.htm

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;


       location / {
          index index.php index.html index.htm;
        try_files $uri $uri/ /index.php?$args;
     }

      location ~ \.php$ {
        # SECURITY : Zero day Exploit Protection
        # try_files $uri =404;
        # ENABLE : Enable PHP, listen fpm sock
         fastcgi_split_path_info ^(.+\.php)(/.+)$;
         fastcgi_pass unix:/run/php-fpm/www.sock;
         fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
         include fastcgi_params;
         } 

        location = /robots.txt {
          allow all;
          log_not_found off;
          access_log off;
        }

    # YOU MAY HAVE OTHER WORDPRESS SUBDIRECTORIES RATHER THAN SUBDOMAINS
    # for instance, https://mywebsite.com/blog could use location /blog { etc.
    # location /OTHER_DIRECTORY_YOU_MAY_USE {
    # try_files $uri $uri/ /OTHER_DIRECTORY_YOU_MAY_USE/index.php?$args /OTHER_DIRECTORIES_YOU_MAY_USE/index.php?q=$uri&$args;
    # }

        error_page 404 /404.html;
         location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }

}


# Settings for a TLS enabled server.

    server {
        listen       443 ssl http2;
        listen       [::]:443 ssl http2;
        server_name  domain.com;
        root         /var/www/html;

        ssl_certificate "/etc/pki/tls/certs/photographybyshaw_au.crt";
        ssl_certificate_key "/etc/pki/tls/private/photographybyshaw_au.key";
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers EECDH+CHACHA20:EECDH+AES;
        ssl_ecdh_curve X25519:prime256v1:secp521r1:secp384r1;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
     #  ssl_ciphers PROFILE=SYSTEM;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

          location / {
                index index.php index.html index.htm;
        try_files $uri $uri/ /index.php?$args;
        
        }

        location ~ \.php$ {
        # SECURITY : Zero day Exploit Protection
        # try_files $uri =404;
        # ENABLE : Enable PHP, listen fpm sock
          fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        } 

        location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
        try_files $uri /index.php?$args;
        }

# Various other include files we can build - w3tc plugin, deny ip addresses from other countries or bad IPs, general settings, or database security:
# include /etc/nginx/w3tc.conf;
# include /etc/nginx/deny.conf;
# include /etc/nginx/inc.conf;
# include /etc/nginx/db.conf;


# example of using subdirectories for Yoast SEO
# rewrite ^/OTHER_DIRECTORY_YOU_MAY_USE/sitemap_index\.xml$ /index.php?sitemap=1 last;
# rewrite ^/OTHER_DIRECTORy_YOU_MAY_USE/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;

# How to block URL strings except for your own static IP address. e.g. https://mydomain.com/private --> USE YOUR OWN STATIC IP ADDRESS

             location /private {
             allow xxx.xxx.xxx.xxx;
             deny all;
             try_files $uri $uri/ /index.php?$query_string;
             }
            
            # OTHER_DIRECTORY_YOU_MAY_USE - e.g. /data or /data/archive or /data/archive/shop
            # would be things ike location /data, or location /data/archive and so on.
              # location /OTHER_DIRECTORY_YOU_MAY_USE {
            # try_files $uri $uri/ /OTHER_DIRECTORY_YOU_MAY_USE/index.php?$args /archive/redbox/index.php?q=$uri&$args;
            # }

         # You can create yur own 404.html page if you wish.
         error_page 404 /404.html;
         location = /404.html {
         }

         error_page 500 502 503 504 /50x.html;
         location = /50x.html {
         }

    }

}

 

The following can be included for gzip if you are not using W3TC or some other plugin that sets the use of gzip.
Place these settings in the SSL 443 server section.

# `gzip` Settings
gzip on;
gzip_disable "msie6";

  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_min_length 256;
  gzip_types
  application/atom+xml
  application/geo+json
  application/javascript
  application/x-javascript
  application/json
  application/ld+json
  application/manifest+json
  application/rdf+xml
  application/rss+xml
  application/xhtml+xml
  application/xml
  font/eot
  font/otf
  font/ttf
  image/svg+xml
  text/css
  text/javascript
  text/plain
  text/xml;

 

Test with nginx -t, then we can add a lot more for tighter security on WordPress. I don’t use protection of hidden files as niginx will not be using a .htaccess file and certbot will not work if the .well-known directory is blocked.

More content can be put in the SSL section before its own last } curly bracket, for WordPress, or we can use include files.

I have shown examples:

cd /etc/nginx

vi inc.conf

# nginx  https://gist.github.com/nfsarmento/57db5abba08b315b67f174cd178bea88
# Disable logging for favicon
location = /favicon.ico {
  try_files /favicon.ico @empty;
  access_log off;
  log_not_found off;
  expires max;
}

location @empty {
  empty_gif;
}

# Enable Rewrite Rules for Yoast SEO SiteMap
rewrite ^/sitemap_index\.xml$ /index.php?sitemap=1 last;
rewrite ^/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;

location ~* .(sh)$ {
   return 444;
}
location ~* /(wp-config.php|readme.html|license.txt|nginx.conf) {
   deny all;
}
# Disallow php in upload folder and add webp rewrite
location /wp-content/uploads/ {
    location ~ \.php$ {
    #Prevent Direct Access Of PHP Files From Web Browsers
        deny all;
    }
    }
# nginx block xmlrpc.php requests
location /xmlrpc.php {
  deny all;
  access_log off;
  log_not_found off;
  return 444;
}

# block access to install.php and upgrade.php.  ---> USE YOUR OWN IP ADDRESS
   location ^~ /wp-admin/install.php {
     deny all;
     allow xxx.xxx.xxx.xxx;
     error_page 403 =404 / ;
   }

# ---> USE YOUR OWN IP ADDRESS
   location ^~ /wp-admin/upgrade.php {
     deny all;
     allow xxx.xxx.xxx.xxx;
     error_page 403 =404 / ;
   }

#Deny access to wp-content folders for suspicious files
location ~* ^/(wp-content)/(.*?)\.(zip|gz|tar|bzip2|7z)\$ {
  deny all;
}

# Stop scann for the follow files on plugins folder
location ~* ^/wp-content/plugins/.+\.(txt|log|md)$ {
      deny all;
      error_page 403 =404 / ;
}

# Stop scann for the follow files on themes folder
location ~* ^/wp-content/themes/.+\.(txt|log|md)$ {
      deny all;
      error_page 403 =404 / ;
}
# Deny access to uploads that aren’t images, videos, music, etc. (js is still needed in Avada)
 location ~* ^/wp-content/uploads/.*.(html|htm|shtml|php|swf)$ {
     deny all;
 }

#This module will allow us to pattern match certain key files and inject random text in the files that
# is non-destructive / non-invasive and will most importantly alter the md5sum calculated on such files. All transparent to WPScan.
location ~* ^/(license.txt|wp-includes/(.*)/.+\.(js|css)|wp-admin/(.*)/.+\.(js|css))$ {
    sub_filter_types text/css text/javascript text/plain;
    sub_filter_once on;
    sub_filter ';' '; /* $msec */ ';
}

#Direct PHP File Access
#If somehow, a hacker successfully sneaks in a PHP file onto your site,
#they’ll be able to run this file by loading file which effectively becomes a backdoor to infiltrate your site.
location ~* /(?:uploads|wp-content|wp-includes)/.*.php$ {
    deny all;
    access_log off;
    log_not_found off;
}
# Similar to PHP file, a dotfile like .htaccess, .user.ini, and .git may contain sensitive information.
# To be on the safer side, it’s better to disable direct access to these files.
location ~ /\.(svn|git)/* {
    deny all;
    access_log off;
    log_not_found off;
}
location ~ /\.ht {
    deny all;
    access_log off;
    log_not_found off;
}
location ~ /\.user.ini {
    deny all;
    access_log off;
    log_not_found off;
}

# Deny backup extensions & log files
location ~* ^.+\.(bak|log|old|orig|original|php#|php~|php_bak|save|swo|swp|sql)$ {
  deny all;
  access_log off;
  log_not_found off;
}

#WordFence
location ~ \.user\.ini$ {
 deny all;
}
# WordPress: deny wp-content, wp-includes php files
location ~* ^/(?:wp-content|wp-includes)/.*\.php$ {
        deny all;
}

# WordPress: deny general stuff
location ~* ^/(?:xmlrpc\.php|wp-links-opml\.php|wp-config\.php|wp-config-sample\.php|wp-comments-post\.php|readme\.html|license\.txt)$ {
        deny all;
}

# Directives to send expires headers and turn off 404 error logging.
location ~* ^.+\.(curl|heic|swf|tiff|rss|atom|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
    access_log off;
    log_not_found off;
    expires 30d;
}

# Web fonts send expires headers
location ~* \.(?:eot|otf|ttf|woff|woff2)$ {
  expires 30d;
  access_log off;
  add_header Cache-Control "public";
}

# SVGs & MP4 WEBM send expires headers - this rule is set specific to ns site
location ~* \.(?:svg|svgz|mp4|webm)$ {
  expires 30d;
  access_log off;
  add_header Cache-Control "public";
}
# Media: images, icons, video, audio send expires headers.
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|aac|m4a|mp3|ogg|ogv|webp)$ {
  expires 30d;
  access_log off;
  add_header Cache-Control "public";
}

#  Cache css & js files
 location ~* \.(?:css(\.map)?|js(\.map)?)$ {
     add_header "Access-Control-Allow-Origin" "*";
     access_log off;
     log_not_found off;
     expires 30d;
 }

# CSS and Javascript send expires headers.
 location ~* \.(?:css|js)$ {
   expires 30d;
   access_log off;
   add_header Cache-Control "public";
 }

# HTML send expires headers.
 location ~* \.(html)$ {
   expires 7d;
   access_log off;
   add_header Cache-Control "public";
 }

# Return 403 forbidden for readme.(txt|html) or license.(txt|html) or example.(txt|html) or other common git repository files
location ~*  "/(^$|readme|license|example|README|LEGALNOTICE|INSTALLATION|CHANGELOG)\.(txt|html|md)" {
    deny all;
}

# Deny backup extensions & log files and return 403 forbidden
location ~* "\.(old|orig|original|php#|php~|php_bak|save|swo|aspx?|tpl|sh|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rdf)$" {
    deny all;
}

[save and exit]

vi db.conf

# common nginx configuration to block sql injection and other attacks
# DISABLE THIS INCLUDES FILE FOR UPDATING PHPMYADMIN CONTENT
location ~* "(eval\()" {
    deny all;
}
location ~* "(127\.0\.0\.1)" {
    deny all;
}
location ~* "([a-z0-9]{2000})" {
    deny all;
}
location ~* "(javascript\:)(.*)(\;)" {
    deny all;
}

location ~* "(base64_encode)(.*)(\()" {
    deny all;
}
location ~* "(GLOBALS|REQUEST)(=|\[|%)" {
    deny all;
}
location ~* "(<|%3C).*script.*(>|%3)" {
    deny all;
}
location ~ "(\\|\.\.\.|\.\./|~|`|<|>|\|)" {
    deny all;
}
location ~* "(boot\.ini|etc/passwd|self/environ)" {
    deny all;
}
location ~* "(thumbs?(_editor|open)?|tim(thumb)?)\.php" {
    deny all;
}
location ~* "(\'|\")(.*)(drop|insert|md5|select|union)" {
    deny all;
}
location ~* "(https?|ftp|php):/" {
    deny all;
}
location ~* "(=\\\'|=\\%27|/\\\'/?)\." {
    deny all;
}
location ~ "(\{0\}|\(/\(|\.\.\.|\+\+\+|\\\"\\\")" {
    deny all;
}
location ~ "(~|`|<|>|:|;|%|\\|\s|\{|\}|\[|\]|\|)" {
    deny all;
}
location ~* "/(=|\$&|_mm|(wp-)?config\.|cgi-|etc/passwd|muieblack)" {
    deny all;
}

location ~* "(&pws=0|_vti_|\(null\)|\{\$itemURL\}|echo(.*)kae|etc/passwd|eval\(|self/environ)" {
    deny all;
}
location ~* "/(^$|mobiquo|phpinfo|shell|sqlpatch|thumb|thumb_editor|thumbopen|timthumb|webshell|config|settings|configuration)\.php" {
    deny all;
}


[save and exit]

vi w3tc.conf


# BEGIN W3TC Minify cache
location ~ /wp-content/cache/minify/.*js_gzip$ {
    gzip off;
    types {}
    default_type application/x-javascript;
    add_header Content-Encoding gzip;
    expires 31536000s;
    etag on;
    if_modified_since exact;
    add_header Pragma "public";
    add_header Cache-Control "public";
    add_header X-Powered-By "W3 Total Cache/2.7.1";
    add_header Referrer-Policy "no-referrer-when-downgrade";
    add_header Vary "Accept-Encoding";
}
location ~ /wp-content/cache/minify/.*css_gzip$ {
    gzip off;
    types {}
    default_type text/css;
    add_header Content-Encoding gzip;
    expires 31536000s;
    etag on;
    if_modified_since exact;
    add_header Pragma "public";
    add_header Cache-Control "public";
    add_header X-Powered-By "W3 Total Cache/2.7.1";
    add_header Referrer-Policy "no-referrer-when-downgrade";
    add_header Vary "Accept-Encoding";
}
# END W3TC Minify cache
# BEGIN W3TC Page Cache cache
location ~ /wp-content/cache/page_enhanced.*gzip$ {
    gzip off;
    types {}
    default_type text/html;
    add_header Content-Encoding gzip;
    expires 3600s;
    etag on;
    if_modified_since exact;
    add_header Pragma "public";
    add_header Cache-Control "public";
    add_header X-Powered-By "W3 Total Cache/2.7.1";
    add_header Referrer-Policy "no-referrer-when-downgrade";
}
# END W3TC Page Cache cache
# BEGIN W3TC Browser Cache
gzip on;
gzip_types text/css text/x-component application/x-javascript application/javascript text/javascript text/x-js text/richtext text/plain text/xsd text/xsl text/xml image/bmp application/java application/msword application/vnd.ms-fontobject application/x-msdownload image/x-icon application/json application/vnd.ms-access video/webm application/vnd.ms-project application/x-font-otf application/vnd.ms-opentype application/vnd.oasis.opendocument.database application/vnd.oasis.opendocument.chart application/vnd.oasis.opendocument.formula application/vnd.oasis.opendocument.graphics application/vnd.oasis.opendocument.spreadsheet application/vnd.oasis.opendocument.text audio/ogg application/pdf application/vnd.ms-powerpoint image/svg+xml application/x-shockwave-flash image/tiff application/x-font-ttf audio/wav application/vnd.ms-write application/font-woff application/font-woff2 application/vnd.ms-excel;
location ~ \.(css|htc|less|js|js2|js3|js4)$ {
    expires 31536000s;
    etag on;
    if_modified_since exact;
    add_header Pragma "public";
    add_header Cache-Control "public";
    add_header X-Powered-By "W3 Total Cache/2.7.1";
    add_header Referrer-Policy "no-referrer-when-downgrade";
    try_files $uri $uri/ /index.php?$args;
}
location ~ \.(html|htm|rtf|rtx|txt|xsd|xsl|xml)$ {
    expires 3600s;
    etag on;
    if_modified_since exact;
    add_header Pragma "public";
    add_header Cache-Control "public";
    add_header X-Powered-By "W3 Total Cache/2.7.1";
    add_header Referrer-Policy "no-referrer-when-downgrade";
    try_files $uri $uri/ /index.php?$args;
}
location ~ \.(asf|asx|wax|wmv|wmx|avi|avif|avifs|bmp|class|divx|doc|docx|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|webp|json|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|webm|mpp|_otf|odb|odc|odf|odg|odp|ods|odt|ogg|ogv|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|tif|tiff|_ttf|wav|wma|wri|xla|xls|xlsx|xlt|xlw|zip)$ {
    expires 31536000s;
    etag on;
    if_modified_since exact;
    add_header Pragma "public";
    add_header Cache-Control "public";
    add_header X-Powered-By "W3 Total Cache/2.7.1";
    add_header Referrer-Policy "no-referrer-when-downgrade";
    try_files $uri $uri/ /index.php?$args;
}
add_header Referrer-Policy "no-referrer-when-downgrade";
# END W3TC Browser Cache
# BEGIN W3TC Minify core
set $w3tc_enc "";
if ($http_accept_encoding ~ gzip) {
    set $w3tc_enc _gzip;
}
if (-f $request_filename$w3tc_enc) {
    rewrite (.*) $1$w3tc_enc break;
}
rewrite ^/wp-content/cache/minify/ /index.php last;
# END W3TC Minify core
# BEGIN W3TC Page Cache core
set $w3tc_query_string $query_string;
if ($w3tc_query_string ~* "^(.*?&|)_branch_match_id(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)_bta_c(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)_bta_tid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)_ga(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)_gl(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)_ke(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)adgroupid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)adid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)age\-verified(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)ao_noptimize(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)campaignid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)campid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)cn\-reloaded(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)customid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)dm_i(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)ef_id(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)epik(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)fb_action_ids(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)fb_action_types(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)fb_source(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)fbclid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)gclid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)gclsrc(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)gdffi(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)gdfms(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)gdftrk(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)hsa_acc(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)hsa_ad(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)hsa_cam(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)hsa_grp(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)hsa_kw(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)hsa_mt(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)hsa_net(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)hsa_src(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)hsa_tgt(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)hsa_ver(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)igshid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)matomo_campaign(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)matomo_cid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)matomo_content(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)matomo_group(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)matomo_keyword(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)matomo_medium(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)matomo_placement(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)matomo_source(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mc_cid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mc_eid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mkcid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mkevt(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mkrid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mkwid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)msclkid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mtm_campaign(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mtm_cid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mtm_content(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mtm_group(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mtm_keyword(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mtm_medium(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mtm_placement(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)mtm_source(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)pcrid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)piwik_campaign(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)piwik_keyword(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)piwik_kwd(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)pk_campaign(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)pk_cid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)pk_content(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)pk_keyword(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)pk_kwd(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)pk_medium(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)pk_source(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)pp(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)redirect_log_mongo_id(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)redirect_mongo_id(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)ref(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)s_kwcid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)sb_referer_host(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)si(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)sscid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)toolid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)trk_contact(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)trk_module(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)trk_msg(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)trk_sid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)usqp(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)utm_campaign(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)utm_content(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)utm_expid(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)utm_id(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)utm_medium(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)utm_source(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~* "^(.*?&|)utm_term(=[^&]*)?(&.*|)$") {
    set $w3tc_query_string $1$3;
}
if ($w3tc_query_string ~ ^[?&]+$) {
    set $w3tc_query_string "";
}
set $w3tc_request_uri $request_uri;
if ($w3tc_request_uri ~* "^([^?]+)\?") {
    set $w3tc_request_uri $1;
}
set $w3tc_rewrite 1;
if ($request_method = POST) {
    set $w3tc_rewrite 0;
}
if ($w3tc_query_string != "") {
    set $w3tc_rewrite 0;
}
set $w3tc_slash "";
if ($w3tc_request_uri ~ \/$) {
    set $w3tc_slash _slash;
}
if ($http_cookie ~* "(comment_author|wp\-postpass|w3tc_logged_out|wordpress_logged_in|wptouch_switch_toggle)") {
    set $w3tc_rewrite 0;
}
set $w3tc_preview "";
if ($http_cookie ~* "(w3tc_preview)") {
    set $w3tc_preview _preview;
}
set $w3tc_ssl "";
if ($scheme = https) {
    set $w3tc_ssl _ssl;
}
if ($http_x_forwarded_proto = 'https') {
    set $w3tc_ssl _ssl;
}
set $w3tc_enc "";
if ($http_accept_encoding ~ gzip) {
    set $w3tc_enc _gzip;
}
if (!-f "$document_root/wp-content/cache/page_enhanced/$http_host/$w3tc_request_uri/_index$w3tc_slash$w3tc_ssl$w3tc_preview.html$w3tc_enc") {
  set $w3tc_rewrite 0;
}
if ($w3tc_rewrite = 1) {
    rewrite .* "/wp-content/cache/page_enhanced/$http_host/$w3tc_request_uri/_index$w3tc_slash$w3tc_ssl$w3tc_preview.html$w3tc_enc" last;
}
# END W3TC Page Cache core


[save and exit]

[Here is an examle of a subdomain include file. I have shown the subdomain aws.domain.com.]

vi aws.conf


    server {
        listen       80;
        listen       [::]:80;
        server_name  aws.domain.com www.aws.domain.com;
        return 301 https://$host$request_uri;
        root         /var/www/aws;

    # add_header X-Frame-Options "SAMEORIGIN";
    # add_header X-Content-Type-Options "nosniff";
    index index.php index.html index.htm

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

    location / {
            index index.php index.html index.htm;
        try_files $uri $uri/ /index.php?$args;
     }

                            location ~ \.php$ {
        # SECURITY : Zero day Exploit Protection
        # try_files $uri =404;
        # ENABLE : Enable PHP, listen fpm sock

         fastcgi_split_path_info ^(.+\.php)(/.+)$;
         fastcgi_pass unix:/run/php-fpm/www.sock;
         fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
         include fastcgi_params;
         } 

        location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
        }

        error_page 404 /404.html;
         location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }

}


    server {
        listen       443 ssl http2;
        listen       [::]:443 ssl http2;
        server_name  aws.domain.com www.domain.com;
        root         /var/www/aws;

        ssl_certificate "/etc/letsencrypt/live/aws.domain.com/fullchain.pem";
        ssl_certificate_key "/etc/letsencrypt/live/aws.domain.com/privkey.pem";
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers EECDH+CHACHA20:EECDH+AES;
        ssl_ecdh_curve X25519:prime256v1:secp521r1:secp384r1;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;

            location / {
                index index.php index.html index.htm;
        try_files $uri $uri/ /index.php?$args;
        }

                            location ~ \.php$ {
        # SECURITY : Zero day Exploit Protection
        # try_files $uri =404;
        # ENABLE : Enable PHP, listen fpm sock
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        } 

        location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
        try_files $uri /index.php?$args;
        }

include /etc/nginx/inc.conf;
include /etc/nginx/w3tc.conf;


         error_page 404 /404.html;
         location = /404.html {
         }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }



}

[save and exit]

[You will notice each domain config file needs its own include files]

[Here is an extract of county blocking. IP2location can give you the IP addresses. We use "deny IP;" not the .htaccess form of "deny from IP;"

vi deny.conf

# deny  russia, china, iran, turkey, north korea;
deny  2.16.20.0/23;
deny  2.16.53.0/24;
deny  2.16.103.0/24;

[and so on. save & exit]

Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

WordPress will no longer use the .htaccess file.
W3TC plugin will ask you to manually create certain files.
As the above configs use nginx owner and group, phpmyadmin should work and allow you to edit database entries.
The WordPress files need nginx owner and group permissions or plugins and other things will not work.
You may not define 256M in wp-config.php or autosave.

I used the following commands to install certbot.

For a subdomain called aws located under /var/www/aws (yours would likely be a differecnt directory)

/usr/bin/certbot -v certonly --non-interactive --agree-tos -m YOUR_EMAIL@gmail.com   -d aws.domain.com --webroot -w /var/www/aws

For a primary domain:

/usr/bin/certbot -v certonly --non-interactive --agree-tos -m YOUR_EMAIL@gmail.com   -d domain.com --webroot -w /var/www/domain.com

Here is a renwal script. First create an empty file certbot.dat, and for the example aws subdomain, certbot_aws.dat with the numeral 0 on the first line only. Chmod to 777.
The following shell script needs owner root, group ec2-user, chmod 777. Use your own domain names. Also create a writable info.log file. USe your own website root directories.

vi /home/ec2-user/certbot.sh

#!/bin/sh

# For primary domain name, if using certbot:
d=`date`
c1=`head -1 /home/ec2-user/certbot.dat`
let c1=$c1+1
if [ "$c1" = "65" ] ;
then
echo "0" > /home/ec2-user/certbot.dat
echo "Certbot domain.com renewal" $d >> /home/ec2-user/info.log

/usr/bin/certbot -v certonly --non-interactive --agree-tos -m YOUR_EMAIL@gmail.com   -d domain.com --webroot -w /var/www/html >/dev/null 2>&1

sudo /usr/bin/systemctl restart nginx >/dev/null 2>&1
sleep 2
echo "Valid dates domain.com:" >> /home/ec2-user/info.log
sudo /usr/bin/openssl x509 -noout -dates -in /etc/letsencrypt/live/domain.com/cert.pem >> /home/ec2-user/info.log

else
echo "Certbot domain.com day $c1 of 65" >> /home/ec2-user/info.log
echo $c1 > /home/ec2-user/certbot.dat
fi

# For a second or subdomain - as an example, using certbot_aws.dat. I show the root as /var/www/aws, but likely you would have /var/www/aws or whatever name, e.g. /var/www/subdomain.domain.com

d=`date`
c1=`head -1 /home/ec2-user/certbot_aws.dat`
let c1=$c1+1
if [ "$c1" = "65" ] ;
then
echo "0" > /home/ec2-user/certbot_aws.dat
echo "Certbot aws.domain.com renewal" $d >> /home/ec2-user/info.log

/usr/bin/certbot -v certonly --non-interactive --agree-tos -m YOUR_EMAIL@gmail.com   -d aws.domain.com --webroot -w /var/www/aws >/dev/null 2>&1

sudo /usr/bin/systemctl restart nginx >/dev/null 2>&1
sleep 2
echo "Valid dates aws.domain.com:" >> /home/ec2-user/info.log
sudo /usr/bin/openssl x509 -noout -dates -in /etc/letsencrypt/live/aws.domain.com/cert.pem >> /home/ec2-user/info.log

else
echo "Certbot openec2.com day $c1 of 65" >> /home/ec2-user/info.log
echo $c1 > /home/ec2-user/certbot_aws.dat
fi

exit

[save and exit]

You can test with the --dry-run option.

crontab entry:

# 1:15am each day check certbot and restart nginx if new certificate
15 1 * * * /home/ec2-user/certbot.sh
# I also like to check the disk swap space is ok:
# midnight each day - check swap space is ok
0 0 * * * /home/ec2-user/services.sh >/dev/null 2>&1



vi services.sh

#!/bin/sh
let g=350
let f=0
h=`free -m|grep Swap|awk '{print $3}'`
let f="$h"
if [ $f -le $g ] ; then
        :
else
 d=`date`
 /usr/bin/systemctl stop nginx
 /usr/bin/systemctl stop mariadb
 /usr/bin/systemctl stop php-fpm
 /usr/bin/systemctl start php-fpm
 /usr/bin/systemctl start mariadb
 /usr/bin/systemctl start nginx
 k=`free -m|grep Swap|awk '{print $3}'`
 echo services.sh: date: $d freespace before: $h freespace after: $k>> /home/ec2-user/info.log
fi

exit

[save and exit - use your own free -m value. I used 350. I prefer below 300.]

Needless to say, these scripts stop and start the system. A paid certificate would not.



Further NGINX configuration changes

None at this stage. All the forum examples of restricting wp-login.php to an allowed IP address have not worked, nor has .htpasswd.
The solution at this stage is any WordPress plugin that limits login attempts.