
NGINX configuration
We are going to be using several template files for our NGINX configuration. The first template is called roles/stack-config/templates/nginx-nginx.conf.j2, and it will replace the main NGINX configuration deployed by the package installation:
# {{ ansible_managed }}
user nginx;
worker_processes {{ ansible_processor_count }};
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
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;
keepalive_timeout 65;
client_max_body_size 20m;
include /etc/nginx/conf.d/*.conf;
}
The content of the file itself is pretty much the same as the initial file, except that we are updating worker_processes so that it uses the number of processors detected by Ansible when the setup module runs, rather than a hardcoded value.
The task to deploy the configuration file is as you would expect and it should be placed in roles/stack-config/tasks/main.yml:
- name: copy the nginx.conf to /etc/nginx/
template:
src: "nginx-nginx.conf.j2"
dest: "/etc/nginx/nginx.conf"
notify: "restart nginx"
As you can see, we are notifying the restart nginx handler, which is stored in the following roles/stack-config/handlers/main.yml file:
- name: "restart nginx"
service:
name: "nginx"
state: "restarted"
enabled: "yes"
Next up, we have the default site template, roles/stack-config/templates/nginx-confd-default.conf.j2:
# {{ ansible_managed }}
upstream {{ php.upstream }} {
server {{ php.ip }}:{{ php.port }};
}
server {
listen 80;
server_name {{ ansible_nodename }};
root {{ wordpress_system.home }};
index index.php index.html index.htm;
include global/restrictions.conf;
include global/wordpress_shared.conf;
}
To help identify where the template files will be placed on the target host, I am naming them so that the full path is in the filename. In this case, the filename is nginx-confd-default.conf.j2 and it will be deployed to /etc/nginx/conf.d/...
The next two templates we are deploying are going into a folder that doesn't exist. So, we first need to create the destination folder. To do this, we need to add the following to roles/stack-config/tasks/main.yml:
- name: create the global directory in /etc/nginx/
file:
dest: "/etc/nginx/global/"
state: "directory"
mode: "0644"
Then, the following command will copy the files to the global folder:
- name: copy the restrictions.conf to /etc/nginx/global/
copy:
src: "nginx-global-restrictions.conf"
dest: "/etc/nginx/global/restrictions.conf"
notify: "restart nginx"
- name: copy the wordpress_shared.conf to /etc/nginx/global/
template:
src: "nginx-global-wordpress_shared.conf.j2"
dest: "/etc/nginx/global/wordpress_shared.conf"
notify: "restart nginx"
As we are not making any replacements in the nginx-global-restrictions.conf file, we are using the copy module rather than template here; the file is stored in roles/stack-config/files/ and has the following content:
# Do not log robots.txt
location = /robots.txt {
log_not_found off;
access_log off;
}
# If no favicon exists return a 204 (no content error)
location ~* /favicon\.ico$ {
try_files $uri =204;
expires max;
log_not_found off;
access_log off;
}
# Deny access to htaccess files
location ~ /\. {
deny all;
}
# Deny access to some bits wordpress leaves hanging around
location ~* /(wp-config.php|readme.html|license.txt|nginx.conf) {
deny all;
}
# Deny access to .php files in the /wp-content/ directory (including sub-folders)
location ~* ^/wp-content/.*.(php|phps)$ {
deny all;
}
# Allow only internal access to .php files inside wp-includes directory
location ~* ^/wp-includes/.*\.(php|phps)$ {
internal;
}
# Deny access to specific files in the /wp-content/ directory (including sub-folders)
location ~* ^/wp-content/.*.(txt|md|exe)$ {
deny all;
}
# hide content of sensitive files
location ~* \\.(conf|engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\\.php)?|xtmpl)\$|^(\\..*|Entries.*|Repository|Root|Tag|Template)\$|\\.php_ {
deny all;
}
# don't allow other executable file types
location ~* \\.(pl|cgi|py|sh|lua)\$ {
deny all;
}
# hide the wordfence firewall
location ~ ^/\.user\.ini {
deny all;
}
As we are setting php.upstream as a variable, we are using the template module to make sure that our configuration contains the correct value, the file roles/stack-config/templates/nginx-global-wordpress_shared.conf.j2 contains the following:
# http://wiki.nginx.org/WordPress
# This is cool because no php is touched for static content.
# Include the "?$args" part so non-default permalinks doesn't break when using query string
location / {
try_files $uri $uri/ /index.php?$args;
}
# Set the X-Frame-Options
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Xss-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
# Do not log + cache images, css, js, etc
location ~* \.(ico|css|js|gif|jpeg|jpg|png|woff|ttf|otf|svg|woff2|eot)$ {
expires max;
log_not_found off;
access_log off;
# Send the all shebang in one fell swoop
tcp_nodelay off;
# Set the OS file cache
open_file_cache max=1000 inactive=120s;
open_file_cache_valid 45s;
open_file_cache_min_uses 2;
open_file_cache_errors off;
}
# Handle .php files
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include /etc/nginx/fastcgi_params;
fastcgi_connect_timeout 180s;
fastcgi_send_timeout 180s;
fastcgi_read_timeout 180s;
fastcgi_intercept_errors on;
fastcgi_max_temp_file_size 0;
fastcgi_pass {{ php.upstream }};
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
}
# Rewrite rules for WordPress SEO by Yoast
rewrite ^/sitemap_index\.xml$ /index.php?sitemap=1 last;
rewrite ^/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;
# Add trailing slash to */wp-admin requests
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
The final part of the NGINX configuration is to copy the main configuration for the WordPress site. The task in roles/stack-config/tasks/main.yml looks as follows:
- name: copy the default.conf to /etc/nginx/conf.d/
template:
src: "nginx-confd-default.conf.j2"
dest: "/etc/nginx/conf.d/default.conf"
notify: "restart nginx"
As we are setting a few variables, such as the path and domain name, we have the following template file:
# {{ ansible_managed }}
upstream php {
server {{ php.ip }}:{{ php.port }};
}
server {
listen 80;
server_name {{ ansible_nodename }};
root {{ wordpress_system.home }};
index index.php;
include global/restrictions.conf;
include global/wordpress_shared.conf;
}
As you can see, we are using a few variables we haven't defined yet, php.ip and php.port. We are going to look at configuring PHP-FPM next.