# How to Install WordPress with SSL on Debian 12

<div align="left"><figure><img src="https://1405916956-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FlGoRMdUMLwewJYv6sXjF%2Fuploads%2FrPps5XdijqC387SyIPa4%2Fdeb-wp-ssl.png?alt=media&#x26;token=bfe0ade0-2100-45e8-8191-24094f4aa86a" alt="" width="188"><figcaption></figcaption></figure></div>

## Prerequisites

1. A server with Debian 12 OS
2. User privileges: root or non-root user with sudo privileges (no sudo before commands)

## Step 1: Update the system

Before we start with LAMP installation, we need to update the system packages to the latest versions available.

```
apt-get update -y && apt-get upgrade -y
```

## Step 2: Install Apache Web Server

We will start with the Apache web server from the LAMP stack first. To install the Apache Web server execute the following command.

```
apt install apache2 -y
```

Once installed, start and enable the service.

```
systemctl enable apache2 && systemctl start apache2
```

Check if the service is up and running.

```
systemctl status apache2
```

<details>

<summary><strong>You should receive the following output</strong></summary>

<mark style="color:green;">●</mark> apache2.service - The Apache HTTP Server\
Loaded: loaded (/lib/systemd/system/apache2.service; <mark style="color:green;">enabled</mark>; preset: <mark style="color:green;">enabled</mark>)\
Active: active (running) since Wed 2025-03-26 07:09:32 UTC; 32min ago\
Docs: <https://httpd.apache.org/docs/2.4/>\
Main PID: 43425 (apache2)\
Tasks: 7 (limit: 4531)\
Memory: 16.5M\
CPU: 310ms\
CGroup: /system.slice/apache2.service

</details>

## Step 3: Install PHP with dependencies

Next, we will install PHP and unzip for later use.

```
apt-get install php8.2 php8.2-cli php8.2-common php8.2-imap php8.2-redis php8.2-snmp php8.2-xml php8.2-mysqli php8.2-zip php8.2-mbstring php8.2-curl libapache2-mod-php unzip -y
```

Check the installed PHP version.

```
php -v
```

<details>

<summary><strong>You should get the following output</strong></summary>

PHP 8.2.28 (cli) (built: Mar 13 2025 18:21:38) (NTS)\
Copyright (c) The PHP Group\
Zend Engine v4.2.28, Copyright (c) Zend Technologies\
with Zend OPcache v8.2.28, Copyright (c), by Zend Technologies

</details>

## Step 4: Install the MariaDB database server

The last of the LAMP stack is the MariaDB database server.\
Install, enable and start the service, and check the status with this command:

<pre><code><strong>apt install mariadb-server -y &#x26;&#x26; systemctl enable --now mariadb &#x26;&#x26; systemctl status mariadb
</strong></code></pre>

<details>

<summary><strong>You should receive the following output</strong></summary>

<mark style="color:green;">●</mark> mariadb.service - MariaDB 10.11.11 database server\
Loaded: loaded (/lib/systemd/system/mariadb.service; <mark style="color:green;">enabled</mark>; preset: <mark style="color:green;">enabled</mark>)\
Active: <mark style="color:green;">active (running)</mark> since Wed 2025-03-26 07:14:59 UTC; 38min ago\
Docs: man:mariadbd(8)\
<https://mariadb.com/kb/en/library/systemd/>\
Main PID: 45352 (mariadbd)\
Status: "Taking your SQL requests now\..."\
Tasks: 9 (limit: 29909)\
Memory: 201.7M\
CPU: 1.350s\
CGroup: /system.slice/mariadb.service

</details>

## Step 5: Create a WordPress database and user

Next, we need to create a WordPress database, the WordPress user, and grant the permissions for that user to the database.

```
mariadb
```

<pre><code>CREATE USER '<a data-footnote-ref href="#user-content-fn-1">wpuser</a>'@'localhost' IDENTIFIED BY '<a data-footnote-ref href="#user-content-fn-2">YourStrongPasswordHere</a>';
</code></pre>

<pre><code>CREATE DATABASE <a data-footnote-ref href="#user-content-fn-3">wpdatabase</a>;
</code></pre>

<pre><code>GRANT ALL PRIVILEGES ON <a data-footnote-ref href="#user-content-fn-3">wpdatabase</a>.* TO '<a data-footnote-ref href="#user-content-fn-1">wpuser</a>'@'localhost';
</code></pre>

```
FLUSH PRIVILEGES;
```

```
EXIT;
```

Remember to save the credentials for the “wpuser” as you may need it in the future\
Please be aware that this WordPress user has nothing to do with the actual WP admin user and is only used for backend administration.

## Step 6. Download and Install WordPress

Before we install WordPress, we first need to download it in the default Apache document root.

```
cd /var/www/html && wget https://wordpress.org/latest.zip && unzip latest.zip && rm latest.zip
```

Next command sets the correct ownership and permissions for the WordPress files and directories. It assigns www-data ownership, then applies 755 permissions for directories and 644 for files.

```
chown -R www-data:www-data wordpress/ && cd wordpress/ && find . -type d -exec chmod 755 {} \; -o -type f -exec chmod 644 {} \;
```

## Step 7. Configure WordPress

Rename the sample configuration file and open it for editing.

```
mv wp-config-sample.php wp-config.php && nano wp-config.php
```

<details>

<summary>Default <strong>should look similar to this</strong></summary>

// \*\* Database settings - You can get this info from your web host \*\* //\
/\*\* The name of the database for WordPress \*/\
define( 'DB\_NAME', 'wordpress[^3]' );

/\*\* Database username \*/\
define( 'DB\_USER', 'wordpress[^1]' );

/\*\* Database password \*/\
define( 'DB\_PASSWORD', 'YourStrongPasswordHere[^2]' );

</details>

<details>

<summary><strong>Change it to this</strong></summary>

// \*\* Database settings - You can get this info from your web host \*\* //\
/\*\* The name of the database for WordPress \*/\
define( 'DB\_NAME', 'wpdatabase[^3]' );

/\*\* Database username \*/\
define( 'DB\_USER', 'wpuser[^1]' );

/\*\* Database password \*/\
define( 'DB\_PASSWORD', 'YourStrongPasswordHere[^2]' );

</details>

## Step 8: Create Apache Virtual Host File

Navigate to the Apache configuration directory and create/open wordpress.conf for editing.

```
cd /etc/apache2/sites-available/ && nano wordpress.conf
```

Paste the following lines of code, save the file and close it.

```
<VirtualHost *:80>
ServerName yourdomain.com
DocumentRoot /var/www/html/wordpress

<Directory /var/www/html/wordpress>
AllowOverride All
</Directory>

ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
```

Enables the Apache rewrite module and the WordPress site configuration.

```
a2enmod rewrite && a2ensite wordpress.conf
```

Check the syntax.

```
apachectl -t
```

<details>

<summary><strong>You should receive the following output</strong></summary>

AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message\
Syntax <mark style="color:green;">OK</mark>

</details>

Reload the Apache configuration, restart the service, and check its status.

```
systemctl reload apache2 && systemctl restart apache2 && systemctl status apache2
```

Once the Apache service is restarted, you can finish your WordPress installation at [**http://yourdomain.com**](#user-content-fn-4)[^4].

## Step 9: Setup SSL certificate for the domain

If you want to present your domain without the “Not secure” warning, you need to pull a SSL certificate. Let's Encrypt is a Certificate Authority that provides free TLS certificates, making it easy for websites to enable HTTPS encryption and create a more secure Internet for everyone. Let's Encrypt is a project of the nonprofit Internet Security Research Group.

Install Certbot and Dependencies Certbot is the recommended tool for obtaining and renewing SSL certificates from Let’s Encrypt. First, install Certbot and the Apache plugin.

```
apt install certbot python3-certbot-apache -y
```

Obtain the SSL Certificate Now, let’s obtain and install the SSL certificate for your domain.

<pre><code>certbot --apache -d <a data-footnote-ref href="#user-content-fn-4">yourdomain.com</a>
</code></pre>

<details>

<summary><strong>This initiates the certificate setup</strong></summary>

Saving debug log to /var/log/letsencrypt/letsencrypt.log\
Enter email address (used for urgent renewal and security notices)\
(Enter 'c' to cancel): [<mark style="color:yellow;">**youremail@gmail.com**</mark>](#user-content-fn-5)[^5]

***

Please read the Terms of Service at\
<https://letsencrypt.org/documents/LE-SA-v1.5-February-24-2025.pdf>. You must\
agree in order to register with the ACME server. Do you agree?

***

<mark style="color:yellow;">**(Y)es/(N)o: Y**</mark>

***

Would you be willing, once your first certificate is successfully issued, to\
share your email address with the Electronic Frontier Foundation, a founding\
partner of the Let's Encrypt project and the non-profit organization that\
develops Certbot? We'd like to send you email about our work encrypting the web,\
EFF news, campaigns, and ways to support digital freedom.

***

<mark style="color:yellow;">**(Y)es/(N)o: N**</mark>\
Account registered.\
Requesting a certificate for [<mark style="color:yellow;">**yourdomain.com**</mark>](#user-content-fn-4)[^4]

Successfully received certificate.\
Certificate is saved at: /etc/letsencrypt/live/[<mark style="color:yellow;">**yourdomain.com**</mark>](#user-content-fn-4)[^4]/fullchain.pem\
Key is saved at: /etc/letsencrypt/live/[<mark style="color:yellow;">**yourdomain.com**</mark>](#user-content-fn-4)[^4]/privkey.pem\
This certificate expires on 2025-06-24.\
These files will be updated when the certificate renews.\
Certbot has set up a scheduled task to automatically renew this certificate in the background.

Deploying certificate\
Successfully deployed certificate for greendata.dk to /etc/apache2/sites-available/wordpress-le-ssl.conf\
Congratulations! You have successfully enabled HTTPS on [<mark style="color:yellow;">**https://yourdomain.com**</mark>](#user-content-fn-4)[^4]

***

If you like Certbot, please consider supporting our work by:

* Donating to ISRG / Let's Encrypt: <https://letsencrypt.org/donate>
* Donating to EFF: <https://eff.org/donate-le>

***

</details>

Test SSL Installation After the process is complete, test the SSL setup by visiting your domain with [**https://yourdomain.com**](#user-content-fn-4)[^4]

Certbot automatically sets up a cron job for renewing your certificate. However, you can verify this.

```
systemctl list-timers
```

<details>

<summary><strong>This will show a list of timers</strong></summary>

<pre class="language-markup"><code class="lang-markup">NEXT                        LEFT          LAST                        PASSED       UNIT                         ACTIVATES                        ­
Wed 2025-03-26 09:09:00 UTC 11min left    Wed 2025-03-26 08:39:14 UTC 18min ago    phpsessionclean.timer        phpsessionclean.service
Wed 2025-03-26 14:15:07 UTC 5h 17min left Tue 2025-03-25 14:15:06 UTC 18h ago      systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
<a data-footnote-ref href="#user-content-fn-6">Wed 2025-03-26 16:19:08 UTC 7h left       -                           -            certbot.timer                certbot.service</a>
Wed 2025-03-26 18:11:29 UTC 9h left       Wed 2025-03-26 07:15:21 UTC 1h 42min ago apt-daily.timer              apt-daily.service
Thu 2025-03-27 00:00:00 UTC 15h left      Wed 2025-03-26 00:00:04 UTC 8h ago       dpkg-db-backup.timer         dpkg-db-backup.service
Thu 2025-03-27 00:00:00 UTC 15h left      Wed 2025-03-26 00:00:04 UTC 8h ago       logrotate.timer              logrotate.service
Thu 2025-03-27 06:46:35 UTC 21h left      Wed 2025-03-26 06:51:30 UTC 2h 6min ago  apt-daily-upgrade.timer      apt-daily-upgrade.service
Thu 2025-03-27 08:04:34 UTC 23h left      Wed 2025-03-26 04:15:20 UTC 4h 42min ago man-db.timer                 man-db.service
Sun 2025-03-30 03:10:16 UTC 3 days left   -                           -            e2scrub_all.timer            e2scrub_all.service
Mon 2025-03-31 01:04:27 UTC 4 days left   -                           -            fstrim.timer                 fstrim.service
</code></pre>

</details>

Alternatively, you can manually test the renewal process.

```
sudo certbot renew --dry-run
```

<details>

<summary>This shows the simulated renewal process</summary>

root\@site:/var/www/html/wordpress# sudo certbot renew --dry-run\
Saving debug log to /var/log/letsencrypt/letsencrypt.log\
\--------------------------------------------------------------------------------------------------

Processing /etc/letsencrypt/renewal/[<mark style="color:yellow;">**yourdomain.com**</mark>](#user-content-fn-4)[^4].conf\
\--------------------------------------------------------------------------------------------------

Account registered.\
Simulating renewal of an existing certificate for [<mark style="color:yellow;">**yourdomain.com**</mark>](#user-content-fn-4)[^4]\
\--------------------------------------------------------------------------------------------------

Congratulations, all simulated renewals <mark style="color:green;">succeeded</mark>:\
/etc/letsencrypt/live/[<mark style="color:yellow;">**yourdomain.com**</mark>](#user-content-fn-4)[^4]/fullchain.pem (<mark style="color:green;">success</mark>)\
\--------------------------------------------------------------------------------------------------

</details>

## Step 10: Troubleshooting

If there’s an issue with the SSL certificate, check Apache’s error logs.

```
journalctl -u apache2
```

[^1]: your custom username

[^2]: your custom password

[^3]: your custom database name

[^4]: your custom domain

[^5]: your custom email

[^6]: This is the Certbot timer
