Note: I’m using this approach in this blog.
The problem
I have been using jekyll for a while. Jekyll is great: integrates with github, allows to statically generate blogs and sites so you just serve static files lightning-fast and you don’t have to be worried about being hacked by bad written php scripts.
The main problem with jekyll is that you have to write markdown manually, and you have to do a lot of things manually. Like copying images, linking to them, choosing the date. And you cannot schedule some posts to be published in the future. So you are kinda limited.
With wordpress you can schedule posts, and it has a wysiwyg editor. Also it has tons of plugins and themes already created for it.
I have tried blogger too, but it is too limited and they do not handle well things like inserting code, which is a thing a do a lot.
So can we have best of both worlds easily in our own server? Yes, but it is not trivial.
The solution
I’m going to explain how to configure everything to have a wordpress working in a password-protected subdomain, and then configure the main domain to cache and serve requests from the uncached domain and to limit access to wordpress admin stuff for security. Everything automatically sandboxed and secured by docker containers and letsencrypt powered SSL.
Links:
https://github.com/jwilder/nginx-proxy
https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion
With this script you are configuring a docker container + a nginx-letsencrypt companion that will renew automatically all the required certificates:
DIR=`dirname $0`
docker network create --driver bridge reverse-proxy
docker rm -f nginx
docker run -d -p 80:80 -p 443:443 \
--name=nginx \
--restart=always \
--network=reverse-proxy \
-v $DIR/certs:/etc/nginx/certs:ro \
-v $DIR/conf.d:/etc/nginx/conf.d \
-v $DIR/vhost.d:/etc/nginx/vhost.d \
-v $DIR/html:/usr/share/nginx/html \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
-e NGINX_PROXY_CONTAINER=nginx \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true \
jwilder/nginx-proxy
docker rm -f nginx-letsencrypt
docker run -d \
--name nginx-letsencrypt \
--restart=always \
--network=reverse-proxy \
--volumes-from nginx \
-v $DIR/certs:/etc/nginx/certs:rw \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
jrcs/letsencrypt-nginx-proxy-companion
Then you have to create a folder for your wordpress instance. And place these files there:
Dockerfile (custom wordpress increasing file limits):
Dockerfile
FROM wordpress
RUN echo "file_uploads = On\n" \
"memory_limit = 64M\n" \
"upload_max_filesize = 64M\n" \
"post_max_size = 64M\n" \
"max_execution_time = 60\n" \
> /usr/local/etc/php/conf.d/uploads.ini
>
docker-compose.yml (you have to replace myblog.com and myemail@myblog.com):
version: '2'
services:
db-wordpress:
restart: always
image: mariadb
volumes:
- $PWD/db-wordpress:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=wordpress
- MYSQL_DATABASE=wordpress
- MYSQL_USER=wordpress
- MYSQL_PASSWORD=wordpress
networks:
- backend
wordpress:
depends_on:
- db-wordpress
#image: wordpress
build:
context: ""
dockerfile: Dockerfile
volumes:
- $PWD/www:/var/www/html
- $PWD/pass:/var/www/pass
#- uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
expose:
- 80
environment:
- WORDPRESS_DB_HOST=db-wordpress:3306
- WORDPRESS_DB_PASSWORD=wordpress
- VIRTUAL_HOST=admin.blog.myblog.com
- LETSENCRYPT_HOST=admin.blog.myblog.com
- LETSENCRYPT_EMAIL=myemail@myblog.com
networks:
- reverse-proxy
- backend
restart: always
cache:
image: nginx
networks:
- reverse-proxy
restart: always
volumes:
- $PWD/cache:/usr/share/nginx/cache
- $PWD/conf.d:/etc/nginx/conf.d:ro
expose:
- 80
environment:
VIRTUAL_HOST: blog.myblog.com
LETSENCRYPT_HOST: blog.myblog.com
LETSENCRYPT_EMAIL: myemail@myblog.com
VIRTUAL_PORT: 80
networks:
backend:
reverse-proxy:
external:
name: reverse-proxy
conf.d/default.conf (you have to replace myblog and REPLACE_WITH_BASE64_ENCODED_USER:PASSWORD):
proxy_cache_path /usr/share/nginx/cache levels=1:2 keys_zone=STATIC:10m inactive=48h max_size=5g;
server {
listen 80;
server_name localhost;
location /wp-admin {
return 500 'Internal Error';
}
location ~* \.(php)$ {
return 500 'Internal Error';
}
location / {
add_header Allow "GET, HEAD" always;
if ( $request_method !~ ^(GET|HEAD)$ ) {
return 405;
}
proxy_pass https://admin.blog.myblog.com/;
proxy_set_header Authorization "Basic REPLACE_WITH_BASE64_ENCODED_USER:PASSWORD";
proxy_cache STATIC;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 2m;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on;
proxy_cache_revalidate on;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
pass/.htpasswd
user:myencryptedpassword
Create missing folders:
# First time you up the docker-compose, these folders will be populated
mkdir db-wordpress
mkdir www
Once you have all this done, you can just:
docker-compose up -d
In order to create a proper password for your .htpasswd for your site, you can use htpasswd tool:
docker-compose exec wordpress bash
# Inside the instance
cd /var/www/pass
htpasswd .htpasswd myuser
wp-config.php:
You will have to set your home/siteurl url for this to work. If you are using sub_filter, you can specify your admin url. If not, you can use relative paths, but that would cause some problems with some plugins.
<?php
//define('WP_HOME','https://admin.blog.myblog.com');
//define('WP_SITEURL','https://admin.blog.myblog.com');
define('WP_HOME','/');
define('WP_SITEURL','/');
sub_filter to the rescue:
proxy_set_header Accept-Encoding ""; // Prevents downloading gzip (and sub_filter would use the gzipped stream)
sub_filter_once off;
sub_filter_types text/html; // If you have generated css/js maybe you want to add them too here
sub_filter 'admin.blog.myblog.com' 'blog.myblog.com';
Since we are limiting all our requests to HEAD and GET, we would want also to externalize our comment system. We can use either disqus, google+ comments or facebook comments or any other option that fits you.