Private
Public Access
1
0

Initial commit: complete WebsiteBox project

Docker-based self-hosted WordPress deployment system with:
- Four-container stack (nginx, wordpress/php-fpm, mariadb, certbot)
- Automatic SSL via Let's Encrypt with self-signed fallback
- First-boot WordPress setup via WP-CLI (GeneratePress + child theme, plugins)
- Interactive setup wizard and one-line install script
- Backup, update, healthcheck, and SSL renewal scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
constantprojects
2026-02-20 15:24:23 -07:00
commit a440026701
32 changed files with 3397 additions and 0 deletions

107
nginx/wordpress-ssl.conf Normal file
View File

@@ -0,0 +1,107 @@
# HTTPS server block — activated after SSL certificates are acquired
server {
listen 443 ssl;
http2 on;
server_name DOMAIN_PLACEHOLDER;
ssl_certificate /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/privkey.pem;
include /etc/nginx/snippets/ssl-params.conf;
root /var/www/html;
index index.php index.html;
# Nginx healthcheck endpoint
location /nginx-health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# ACME challenge (for renewals)
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Rate limit wp-login.php
location = /wp-login.php {
limit_req zone=wplogin burst=3 nodelay;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_param HTTPS on;
}
# Block xmlrpc.php at nginx level
location = /xmlrpc.php {
deny all;
return 403;
}
# Block access to sensitive files
location ~ /\. {
deny all;
}
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
# WordPress permalinks
location / {
try_files $uri $uri/ /index.php?$args;
}
# PHP handling via FastCGI to WordPress container
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_param HTTPS on;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_read_timeout 300;
}
# Static file caching
# NOTE: add_header in a location block overrides ALL parent add_header directives,
# so security headers must be repeated here.
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|webp|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
access_log off;
}
}
# HTTP to HTTPS redirect (replaces the HTTP-only block)
server {
listen 80;
server_name DOMAIN_PLACEHOLDER;
# ACME challenge must remain accessible over HTTP
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Nginx healthcheck endpoint
location /nginx-health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
location / {
return 301 https://$host$request_uri;
}
}