WordPress based on NGINX


NGINX & WordPress

Prepare your server system:

mkdir -p /var/www/wordpress

Ammend your NGINX configuration and vhosts:

(1) vi /etc/nginx/nginx.conf

user www-data;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
use epoll;
http {
server_names_hash_bucket_size 64;
upstream php-handler {
server unix:/run/php/php7.2-fpm.sock;
### new ###
real_ip_header X-Forwarded-For;
real_ip_recursive on;
### new ###
include /etc/nginx/mime.types;
include /etc/nginx/proxy.conf;
include /etc/nginx/ssl.conf;
include /etc/nginx/header.conf;
include /etc/nginx/optimization.conf;
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" '
'"$host" sn="$server_name" '
'rt=$request_time '
'ua="$upstream_addr" us="$upstream_status" '
'ut="$upstream_response_time" ul="$upstream_response_length" '
'cs=$upstream_cache_status' ;
access_log /var/log/nginx/access.log main;
sendfile on;
send_timeout 3600;
tcp_nopush on;
tcp_nodelay on;
open_file_cache max=500 inactive=10m;
open_file_cache_errors on;
keepalive_timeout 65;
reset_timedout_connection on;
server_tokens off;
resolver_timeout 10s;
include /etc/nginx/conf.d/*.conf;

(2) vi /etc/nginx/conf.d/nextcloud.conf

server {
listen 80 default_server;
server_name your.dedyn.io;
location ^~ /.well-known/acme-challenge {
proxy_set_header Host $host;
location / {
return 301 https://$host$request_uri;
server {
listen 443 ssl http2 default_server;
server_name your.dedyn.io;
root /var/www/nextcloud/;
access_log /var/log/nginx/nextcloud.access.log main;
error_log /var/log/nginx/nextcloud.error.log warn;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
### new ###
location ^~ /wordpress/ {
client_max_body_size 1G;
proxy_connect_timeout 3600;
proxy_send_timeout 3600;
proxy_read_timeout 3600;
send_timeout 3600;
proxy_buffering on;
proxy_max_temp_file_size 1024m;
proxy_request_buffering on;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
### new ###
client_max_body_size 10240M;
location / {
rewrite ^ /index.php$uri;
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
deny all;
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
location ~ \.(?:flv|mp4|mov|m4a)$ {
mp4_buffer_size 100m;
mp4_max_buffer_size 1024m;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
include php_optimization.conf;
fastcgi_pass php-handler;
fastcgi_param HTTPS on;
location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
include php_optimization.conf;
fastcgi_pass php-handler;
fastcgi_param HTTPS on;
location ~ ^/(?:updater|ocs-provider)(?:$|/) {
try_files $uri/ =404;
index index.php;
location ~ \.(?:css|js|woff|svg|gif|png|html|ttf|ico|jpg|jpeg)$ {
try_files $uri /index.php$uri$is_args$args;
access_log off;
expires 360d;

(3) vi /etc/nginx/conf.d/wordpress.conf

server {
listen default_server;
include /etc/nginx/proxy.conf;
root /var/www/;
charset utf-8;
location ^~ /wordpress {
client_max_body_size 1024M;
index index.php;
access_log /var/log/nginx/wordpress.access.log main;
error_log /var/log/nginx/wordpress.error.log warn;
location /wordpress {
try_files $uri $uri/ /wordpress/index.php$args;
location = /wordpress/favicon.ico {
log_not_found off;
access_log off;
location = /wordpress/robots.txt {
allow all;
log_not_found off;
access_log off;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
include php_optimization.conf;
fastcgi_pass php-handler;
fastcgi_param HTTPS on;
fastcgi_index index.php;
location ~ /wordpress/\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off;
log_not_found off;
expires max;
location /wordpress/wp-admin {
deny all;
location ~ ^/wordpress/wp-content/uploads/.*.(html|htm|shtml|php|js|swf)$ {
deny all;
location ~* /wordpress/wp-config.php {
deny all;

Test your NGINX configuration

nginx -t

and then restart your Webserver

service nginx restart

Download and extract the latest WordPress release:

curl https://wordpress.org/latest.tar.gz | tar xz -C /var/www/wordpress --strip-components=1

Apply the proper permissions:

chown -R www-data:www-data /var/www/wordpress

Configure MariaDB if not already done:

mv /etc/mysql/my.cnf /etc/mysql/my.cnf.bak && vi /etc/mysql/my.cnf

Change the entire my.cnf-file to:

innodb_buffer_pool_size = 128M
innodb_buffer_pool_instances = 1
innodb_flush_log_at_trx_commit = 2
innodb_log_buffer_size = 32M
innodb_max_dirty_pages_pct = 90
query_cache_type = 1
query_cache_limit = 2M
query_cache_min_res_unit = 2k
query_cache_size = 64M
tmp_table_size= 64M
max_heap_table_size= 64M
slow-query-log = 1
slow-query-log-file = /var/log/mysql/slow.log
long_query_time = 1

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mariadb.conf.d/

default-character-set = utf8mb4

character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
binlog_format = MIXED

Create the WordPress database and user:

mysql -uroot -p
CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER wordpress-DBuser@localhost identified by 'wordpress-DBpassword';
GRANT ALL PRIVILEGES on wordpress.* to wordpress-DBuser@localhost;
flush privileges;

Create a new WordPress configuration as user www-data:

sudo -u www-data vi /var/www/wordpress/wp-config.php
if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false)
define('WP_HOME', 'https://your.dedyn.io/wordpress');
define('WP_SITEURL', 'https://your.dedyn.io/wordpress');
define('DB_NAME', 'wordpress');
define('DB_USER', 'wordpress-DBuser');
define('DB_PASSWORD', 'wordpress-DBpassword');
define('DB_HOST', 'localhost');
define('DB_CHARSET', 'utf8mb4');
define('DB_COLLATE', '');
define('AUTH_KEY', '((EG@3h!GS9*-N2r|l<#A5z)cQ31(-p#6?m;|/F->tD(Pqt:K{]>>`H5$/jn`cEs');
define('SECURE_AUTH_KEY', 'N&q/NtSgcOGKx^%~X*(b[,tQM-+`Vp585Cp@I#>8l+FlI][ZV8|&=r/H8(f^*ayM');
define('LOGGED_IN_KEY', 'tAqdyKN`%10md Ibmn|bKC?,Rp&O~THrh!m0|1>l5hpb8/+BbEAe0tev=.hwYmKk');
define('NONCE_KEY', 'Tq|IU,op^i/X-yynDY2$1r!>#97&Ju-v|aSofS^M[,D#:[Y?mQE;]o3<z/4f|,LL');
define('AUTH_SALT', 'N%p#JtQ0A:%(J._-++%{xPy=fIn?ak@ 4ZHmx*GKp/o`pm9;.hqHZ(L9{[+l~I=}');
define('SECURE_AUTH_SALT', 'D/r}aSw@^VT=fijqRc*Rko$._?GSOe==42!|~tI9^(X,7SVYzBF=NKP0[)wfa`T4');
define('LOGGED_IN_SALT', '@Y<ViUa/,T{D25j-:eqva7s;$5.-i3jr.;bF*nm- =9C3WPT+PFHbfP*jLM<q!;E');
define('NONCE_SALT', 'LD2cQ|?C{).Jz.WBIbHZ|m-NG60><dlq$Y)Q]ED?Kn(/,Mlx)&b%5)$<eU`]f+u3');
$table_prefix = 'wp_';
define('WP_DEBUG', false);
if ( !defined('ABSPATH') )
 define('ABSPATH', dirname(__FILE__) . '/');
require_once(ABSPATH . 'wp-settings.php');

Substitute the red ones regarding your dyndns and generate your personal salts here. Please copy and paste your salts to your wp-config.php instead of the above.  Call your WordPress instance in your preferred browser (https://your.dedyn.io/wordpress/) and run the installation wizard … enjoy your WordPress:



10 Responses

  1. Robin says:

    Hi again,
    Does the header.conf , proxy.conf from your nextcloud guide work for wordpress also?

    • yes, you’ll only need a new wordpress.conf (vhost for WordPress)

      • Robin says:

        I have been struggling with the header.conf for WordPress, your Nextcloud header.config don’t work to the fullest with WordPress.
        It’s the X-Frame-Options and Content Security Policy who don’t work with WordPress.

        I have found out that X-Frame-Options can be fixed if you add “add_header X-Frame-Options “SAMEORIGIN” always;” but I havent found out how to fix the Content Security Policy yet any idés?

  2. Robin says:

    If I’ll have my wordpress without /wordpress just access the page like robin.se or something like that how do I adjust that to the vi /etc/nginx/conf.d/wordpress.conf file?
    Also, if I want to use it with https should I just adjust the listen default_server; to listen 80 default_server; and the proxy.conf will handle the re-direct to 443?

    • You have to change Nextclouds vhost to let Nextcloud act in a subfolder as described in my guide and then change the vhost for wordpress accordingly to listen to the ‘/’ (root directory).

  3. Robin says:

    What does this two lines in the nginx.conf does?
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

  4. Kalin says:

    Great tutorial.
    Trying this on Ubuntu 18.04 Server. Do you have any idea how to deal with few SQL errors that are popping on the screen during the final steps. After language selection and filling username and password and pressing “Install WordPress” from the browser I receive:

    WordPress database error: [Unknown character set: ‘utf8mb4_general_ci’]

    WordPress database error: [Unknown character set: ‘utf8mb4_general_ci’]

    Almost endless.
    It’s maybe a stupid question but I am pretty low on terminal skills.


    • I guess, i should have point to the my.cnf as i did in the nextcloud guide. Please verify your my.cnf and substitute the whole file with the example from the guide, then restart mysql/mariadb. If the error would still exist verify the wp-config.php and the value define(‘DB_CHARSET’, ‘utf8mb4’);.

      I already updated the guide.

      • Kalin says:

        Actually I didn’t expect such a prompt answer! I … am speechless!
        It worked great!

        1. Changed the file my.cnf > Did not fixed the problem;
        2. Edited the wp-config.php > and voila – Problem solved!

        Thank you very, veeery much!

Leave a Reply

Your email address will not be published. Required fields are marked *