User Tools

Site Tools


computing:selfhostedwp

This is an old revision of the document!



  • selfhostedwp
  • Jonathan Haack
  • Haack's Networking
  • netcmnd@jonathanhaack.com

selfhostedwp


This tutorial is for setting up a self-hosted WordPress instance on Debian GNU/Linux. This tutorial assumes you have some familiarity setting up a LAMP stack. If you need help with that, check out Apache Survival. Alright, let's install our LAMP stack and required/optional php modules. Make sure to review what your instance requires and don't install or configure modules you don't need.

sudo apt install apache2 mariadb-server php8.x php-common php-cgi php-cli php-zip php-mysql php-mbstring php-intl php-fpm php-curl php-gd php-imagick php-xml php-xmlrpc php-soap php-opcache php-apcu php-bcmath memcached wget unzip

Sometimes dpkg can choose which version of php you want and it's not always the version you want. In those cases, you can explicitly specify the version you need as follows:

sudo apt-get install php8.2-{common,cgi,cli,zip,mysql,mbstring,intl,fpm,curl,gd,imagick,xml,xmlrpc,gpm,soap,opcache,apcu,bcmath}

In this particular configuration, I am not using libapache2-mod-php. Instead I am using mpm_event and php-fpm. This is not necessary for many smaller instances or self-hosted scenarios. If you are new to self-hosting, then in addition to the above steps, you should do sudo apt install libapache2-mod-php8.x and ignore the fpm-based steps below.

sudo apt remove libapache2-mod-php --purge
sudo a2enmod ssl
sudo a2enmod headers
sudo a2enmod cache
sudo a2enmod rewrite
sudo a2enmod setenvif 
sudo a2dismod php8.2
sudo a2dismod mpm_prefork
sudo a2enmod mpm_event
sudo a2enmod proxy
sudo a2enmod proxy_fcgi
sudo a2enconf php8.2-fpm
sudo a2enconf php8.2-cgi
sudo apache2ctl configtest  
sudo systemctl restart apache2
sudo systemctl restart php8.2-fpm

There are two standard ways to configure php-fpm. One of those is to use ProxyPassReverse, however, this will disable the use of .htaccess in your WordPress root which is not ideal. The next common way which I prefer and use here, is to add a FilesMatch condition to your virtual host as follows. Within the <Include> and </Include> portion of your default-ssl.conf virtual host, add something like:

<FilesMatch ".+\.ph(ar|p|tml)$">
    SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
</FilesMatch>

That takes care of configuring php-fpm and mpm_event. Before proceeding, navigate to your tld.domain in a web browser and make sure that your site resolves properly. Now, to make sure that your WordPress index.php file resolves properly to display your home page, make sure to move index.php to the top priority as follows:

sudo nano /etc/apache2/mods-enabled/dir.conf
<DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm>

***

Optionally, we can install phpmyadmin, and if you do, you should secure as follows:

sudo htpasswd -c /etc/apache2/.phpmyadmin phpmyadmin  
sudo nano /usr/share/phpmyadmin/.htaccess

Enter the following in the file that opens:

<AuthType Basic>
<AuthName "Restricted Files">
<AuthUserFile /etc/apache2/.phpmyadmin>
<Require valid-user>

Close and save the file. Let's set up a database now for the WordPress instance:

sudo mysql -u root -p
CREATE DATABASE databasename DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
GRANT ALL ON databasename.* TO 'databaseuser'@'localhost' IDENTIFIED BY 'passwordhere';
FLUSH PRIVILEGES;
EXIT;

Next up, it is time to allow overrides in your primary apache configuration:

sudo nano /etc/apache2/apache2.conf
<Directory /var/www/>
<AllowOverride All>

If you have not set the fully qualified domain name, you may get an error - that can safely be ignored unless you desire it. If you want to get rid of that, navigate to /etc/apache2/apache.conf and enter a ServerName. Otherwise, time to download Word Press:

cd ~/Downloads
mkdir wpdownload
cd wpdownload
curl -O https://wordpress.org/latest.tar.gz
tar xzvf latest.tar.gz
touch ~/Downloads/wpdownload/wordpress/.htaccess
sudo chmod 640 ~/Downloads/wpdownload/wordpress/.htaccess
cp ~/Downloads/wpdownload/wordpress/wp-config-sample.php ~/Downloads/wpdownload/wordpress/wp-config.php
mkdir ~/Downloads/wpdownload/wordpress/wp-content/upgrade

Okay, we will need the files and directories I created once we get it running. Now, let's move the wordpress directory to the proper location for self-hosting.

sudo mv ~/Downloads/wpdownload/wordpress /var/www/site1.com/public_html

Now, let's set up permissions and ownership:

sudo chown -R www-data:www-data /var/www/site1.com/public_html
sudo find /var/www/site1.com/public_html -type d -exec chmod g+s {} \;
sudo chmod 755 /var/www/site1.com/public_html/wp-content
sudo chmod -R 755 /var/www/site1.com/public_html/wp-content/themes
sudo chmod -R 755 /var/www/site1.com/public_html/wp-content/plugins

Ok, time to grab 'secure values' from WP.com and then set up wp-config.php for the installation, and also enter in the database credentials from above:

curl -s https://api.wordpress.org/secret-key/1.1/salt/
sudo nano /var/www/site1.com/public_html/wp-config.php

Let's also add the following line to the wp-config.php file for updates. Note: This only needs to be added if you are not using libapachemod sfaik.

sudo nano /var/www/site1.com/public_html/wp-config.php
<define('FS_METHOD','direct');>

Visit wordpress site and configure by opening a web browser of your choice and entering site1.com. If you need more than one site, but do not want to set up a separate virtual host, for example using subdomain.site1.com, then you should read Word Press Multisite. Optimizing WP is a different matter, for caching and header security, and other best practices, consider the following.

apt install memcached
nano /etc/default/memcached
a2enmod cache

Put this snippet under #Include /etc/proftpd/tls.conf and then restart the service:

sudo systemctl restart proftpd.service

Optimizing and securing WordPress usually boils down to some cache and header settings. Cache and/or page expiry settings:

apt install memcached
nano /etc/default/memcached
a2enmod cache
a2enmod expires
<IfModule mod_expires.c>
        ExpiresActive On
        ExpiresByType image/jpg "access 1 year"
        ExpiresByType image/jpeg "access 1 year"
        ExpiresByType image/gif "access 1 year"
        ExpiresByType image/png "access 1 year"
        ExpiresByType text/css "access 1 week"
        ExpiresByType text/html "access 1 month"
        ExpiresByType text/x-javascript "access 1 week"
        ExpiresDefault "access 1 month"
</IfModule>

Enable re-writes:

a2enmod rewrite
<IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
        RewriteBase /
        RewriteRule ^index\.php$ - [L]
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule . /index.php [L]
        <FilesMatch "\.(js|css|jpe?g|png|gif|eot|otf|svg|ttf|woff2?)$">
                Header set Timing-Allow-Origin "*"
        </FilesMatch>
</IfModule>

Enable headers:

a2enmod headers
<IfModule mod_headers.c>
        Header always set X-Content-Type-Options "nosniff"
        <IfModule mod_setenvif.c>
                SetEnvIf Origin "^(.+)$" CORS=$0
        </IfModule>
        Header set Access-Control-Allow-Origin %{CORS}e env=CORS
        Header set Access-Control-Allow-Credentials "true" env=CORS
        <FilesMatch "\.(php|html)$">
                Header set X-Frame-Options "ALLOW"
                Header set X-XSS-Protection "0"
                Header set X-Download-Options "noopen"
                Header set X-Permitted-Cross-Domain-Policies "none"
                Header set X-DNS-Prefetch-Control "on"
                Header set Pragma "no-cache"
                Header set Age "0"
                Header set Cache-Control ""
                Header set Strict-Transport-Security "max-age=0" env=HTTPS
                Header set Referrer-Policy ""
                Header set Cross-Origin-Embedder-Policy "unsafe-none"
                Header set Cross-Origin-Opener-Policy "unsafe-none"
                Header set Report-To '{"max_age": 0, "endpoints": [{"url": ""}]}'
                Header set Content-Security-Policy "default-src * data:; script-src https: 'unsafe-inline' 'unsafe-eval'; style-src https: 'unsafe-inline'"
                Header set Referrer-Policy "no-referrer-when-downgrade"
                Header set Feature-Policy "camera 'none'; fullscreen 'self'; geolocation *; microphone 'self' https://plaza.pvpfrontier/*"
        </FilesMatch>
</IfModule>

Personally, I don't think anyone should be using ftp or even sftp right now, but many still do. If so, here's how to make an sftp server for updating WordPress that way:

You can optionally require an sftp server instead of using the default installer. Here's an example using proftp, which is still maintained:

sudo apt install proftpd ftp ftp-ssl 
cd /etc/proftpd
sudo openssl req -new -x509 -days 7305 -nodes -out ftpd-rsa.pem -keyout ftpd-rsa-key.pem
sudo nano /etc/proftpd/proftpd.conf
a2enmod tls

<IfModule mod_tls.c>
   TLSEngine on
   TLSLog /var/log/proftpd-tls.log
   TLSProtocol TLSv1
   # Are clients required to use FTP over TLS when talking to this server?
   TLSRequired off
   TLSRSACertificateFile    /etc/proftpd/ftpd-rsa.pem
   TLSRSACertificateKeyFile /etc/proftpd/ftpd-rsa-key.pem
   # Authenticate clients that want to use FTP over TLS?
   TLSVerifyClient off
   TLSOptions NoSessionReuseRequired
</IfModule>

oemb1905 2023/06/29 04:29

computing/selfhostedwp.1688013272.txt.gz · Last modified: 2023/06/29 04:34 by oemb1905