Nextcloud 15, Roundcube, WordPress, Shellinabox and Pi-hole behind a NGINX reverse proxy


If you are interested in running Nextcloud in parallel to Roundcube, WordPress, Shellinabox, Pi-hole and so on behind a NGINX reverse proxy you will find all the neccessary changes and configuration files below as an ammendment to the initial guide (Nextcloud installation guide). This configuration leads to an A+ rating and all Nextcloud checks will be successfully passed. You only have to ammend the red marked values (YOUR.DEDYN.IO, 192.168.2.x, 86) regarding your environment!


First move the existing nginx.conf and create a new file:

sudo -s
mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
vi /etc/nginx/nginx.conf

Paste all the following rows:

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.3-fpm.sock;
}
set_real_ip_from 127.0.0.1;
set_real_ip_from 192.168.2.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
include /etc/nginx/mime.types;
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 208.67.222.222 208.67.220.220 valid=30s;
resolver_timeout 5s;
include /etc/nginx/conf.d/*.conf;
}

Create the new file “gateway.conf” that will handle the incomming http(s)-requests to the server(s) behind the proxy:

vi /etc/nginx/conf.d/gateway.conf

Paste the following rows

server {
listen 80 default_server;
server_name your.dedyn.io;
location ^~ /.well-known/acme-challenge {
proxy_pass http://127.0.0.1:81;
proxy_set_header Host $host;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2 default_server;
server_name your.dedyn.io;
include /etc/nginx/ssl.conf;
include /etc/nginx/header.conf;
### NEXTCLOUD ###
location ^~ / {
client_max_body_size 10240M;
proxy_buffering off;
proxy_connect_timeout 3600;
proxy_max_temp_file_size 10240M;
proxy_pass http://127.0.0.1:82;
proxy_read_timeout 3600;
proxy_redirect off;
proxy_request_buffering off;
proxy_send_timeout 3600;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
send_timeout 3600;
}
### GLOBAL gateway.conf ###
location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}
### OPTIONAL Roundcube ###
location ^~ /roundcube/ {
client_max_body_size 1024M;
proxy_buffering off;
proxy_connect_timeout 3600;
proxy_max_temp_file_size 1024M;
proxy_pass http://127.0.0.1:83;
proxy_read_timeout 3600;
proxy_redirect off;
proxy_request_buffering off;
proxy_send_timeout 3600;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
send_timeout 3600;
}
### OPTIONAL SHELLINABOX ###
location ^~ /shellinabox/ {
#auth_basic "Restricted Area";
#auth_basic_user_file /etc/nginx/.htpasswd; 
proxy_pass http://127.0.0.1:4200;
proxy_read_timeout 90;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
### OPTIONAL WORDPRESS ###
location ^~ /wordpress/ {
client_max_body_size 1024M;
proxy_buffering off;
proxy_connect_timeout 3600;
proxy_max_temp_file_size 1024M;
proxy_pass http://127.0.0.1:84;
proxy_read_timeout 3600;
proxy_redirect off;
proxy_request_buffering off;
proxy_send_timeout 3600;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
send_timeout 3600;
}
### OPTIONAL Pihole ###
location ^~ /pihole {
proxy_pass http://127.0.0.1:86/admin;
proxy_read_timeout 90;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
### END ###
}

Move the already existing nextcloud.conf to nextcloud.conf.bak and create a new one:

mv /etc/nginx/conf.d/nextcloud.conf /etc/nginx/conf.d/nextcloud.conf.bak
vi /etc/nginx/conf.d/nextcloud.conf

Paste all the following rows:

server {
server_name 127.0.0.1;
listen 127.0.0.1:82 default_server;
include /etc/nginx/proxy.conf;
root /var/www/nextcloud/;
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/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;
}
#SOCIAL app enabled? Please uncomment the following three rows
#rewrite ^/.well-known/webfinger /public.php?service=webfinger last;
#rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
#rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;
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|m4v)$ {
mp4;
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 365d;
}
}

Optionally: Roundcube

vi /etc/nginx/conf.d/roundcube.conf

Paste the following rows:

server {
server_name 127.0.0.1;
listen 127.0.0.1:83 default_server;
root /var/www/;
include /etc/nginx/proxy.conf;
client_max_body_size 1024M;
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
charset utf-8;
location ^~ /roundcube {
index index.php;
location ~ ^/favicon.ico$ {
root /var/www/roundcube/skins/default/images;
log_not_found off;
access_log off;
expires max;
}
location ~ ^/roundcube/(README|INSTALL|LICENSE|CHANGELOG|UPGRADING)$ {
deny all;
}
location ~ ^/roundcube/(bin|SQL|config|temp|logs)/ {
deny all;
}
location ~ /roundcube/\.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
include php_optimization.conf;
fastcgi_pass php-handler;
fastcgi_param HTTPS on;
fastcgi_index index.php;
try_files $uri =404;
}
location ~ /roundcube/\.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
}
}

Optionally: WordPress

vi /etc/nginx/conf.d/wordpress.conf

Paste the following rows:

server {
server_name 127.0.0.1;
listen 127.0.0.1:84 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 {
allow 192.168.2.0/24;
deny all;
#auth_basic "Restricted Area";
#auth_basic_user_file /etc/nginx/.htpasswd;
}
location ~ ^/wordpress/wp-content/uploads/.*.(html|htm|shtml|php|js|swf)$ {
deny all;
}
}
}

You have to modify the “/var/www/wordpress/wp-config.php” properly as well:

<?php
if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false)
 $_SERVER['HTTPS']='on';
if(isset($_SERVER['HTTP_X_FORWARDED_HOST']))
 $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
define('WP_HOME', 'https://YOUR.DEDYN.IO/wordpress');
define('WP_SITEURL', 'https://YOUR.DEDYN.IO/wordpress');
define('DB_NAME', 'wpblog');
define('DB_USER', 'wp-db-user');
define('DB_PASSWORD', 'wp-db-pwd');
define('DB_HOST', 'localhost');
define('DB_CHARSET', 'utf8mb4');
define('DB_COLLATE', '');
define('AUTH_KEY', '***');
define('SECURE_AUTH_KEY', '***');
define('LOGGED_IN_KEY', '***');
define('NONCE_KEY', '***');
define('AUTH_SALT', '***');
define('SECURE_AUTH_SALT', '***');
define('LOGGED_IN_SALT', '***');
define('NONCE_SALT', '***');
//define('WP_REDIS_PATH', '/var/run/redis/redis.sock');
//define('WP_CACHE_KEY_SALT', 'wblog_');
//define('WP_CACHE', true);
//define('WP_REDIS_PORT', '0');
//define('WP_REDIS_DATABASE', '0');
//define('WP_REDIS_SCHEME', 'unix');
$table_prefix = 'blog_';
define('WP_DEBUG', false);
if ( !defined('ABSPATH') )
 define('ABSPATH', dirname(__FILE__) . '/');
require_once(ABSPATH . 'wp-settings.php');

Your WordPress-Salts can be generated here.


Modify the Shellinabox configuration "SHELLINABOX_ARGS="...""
vi /etc/default/shellinabox

to

SHELLINABOX_ARGS="--no-beep --localhost-only --disable-ssl"

and restart shellinabox:

service shellinabox restart

Finally ammend the letsencrypt.conf: add “include /etc/nginx/proxy.conf;

server {
server_name 127.0.0.1;
listen 127.0.0.1:81 default_server;
include /etc/nginx/proxy.conf;
charset utf-8;
access_log /var/log/nginx/le.access.log main;
error_log /var/log/nginx/le.error.log warn;
location ^~ /.well-known/acme-challenge {
default_type text/plain;
root /var/www/letsencrypt;
}
}

Now restart NGINX

service nginx restart

and enjoy your Nextcloud, Roundcube and WordPress behind a reverse proxy.


Enjoy your personal data in your secured and hardened Nextcloud-Server!


Don’t forget to backup your Nextcloud

Find more instructions here: Nextcloud backup and restore



Carsten Rieger

Carsten Rieger

Carsten Rieger is a senior system engineer in full-time and also working as an IT freelancer. He is working with linux environments for more than 13 years, an Open Source enthusiast and highly motivated on linux installation and troubleshooting. Mostly working with Debian/Ubuntu Linux, Nginx and Apache web server, MariaDB/MySQL/PostgreSQL, PHP, Cloud infrastructure (e.g. Nextcloud) and other open source projects (e.g. Roundcube) and in voluntary work for the Dr. Michael & Angela Jacobi Stiftung for more than 7 years.