Previously, I looked at setting up Debian 8 with PHP 7.0. But now, Debian 9 is the current stable version and Debian 8 is only due to receive security updates until May 2018. Sounds like a good time to upgrade!

The reason I started to look into upgrading was that I found out that the highest version of Apache available on Debian 8 is Apache 2.4.10 which is not capable of running HTTP/2. Having recently upgraded a lot of my websites to HTTPS, I also wanted to upgrade them to HTTP/2 aswell. Debian 8’s version of Apache was not able to be upgraded without doing so manually. However, Debian 9’s default version of Apache is 2.4.25 which is HTTP/2 capable, it also has PHP 7.0 as the default and has upgraded MySQL to MariaDB. All the more reason to upgrade!

From here

For those who don’t know, Debian codenames are based on the characters in the famous animated movie Toy Story. This release is named after the glittery purple rubber toy octopus, Stretch.

Alternatives to upgrading to Debian 9 (Stretch)

Upgrading from Debian Jessie to Stretch will not be for everyone. Here are some possible alternatives.

Manually installing a higher version of Apache.

This is generally thought of as possible but not recommended. You also have the gradual winding down of Debian support from May 2018 which may lead to security risks.

Changing to another flavor of Linux with a version of Apache that is capable of running HTTP/2.

Ubuntu 16.04 is one version that can run HTTP/2 without too much tinkering.

Ubuntu is similar to Debian in terms of the shell commands. I have some websites that use Ubuntu 16.04 but for this particular website, I wanted to stay with Debian. Ubuntu is thought of as more experimental, while Debian is thought of as more stable and faster.

Ubuntu 16.04 LTS looks like it will be supported until April 2021 from here so it will not need updating any time soon. The next version with long-term support (LTS) is released next month (April 2018) so I may look into upgrading the servers running Ubuntu when that arrives.

I have used AWS in the past and I have built websites on FreeBSD and CentOS servers that I can remember, but currently I am mainly using Debian and Ubuntu with un-managed VPS. This experience has made me want to try some different flavors, or at least learn about how the other flavors are different. What I’ve read so far makes me think that Debian is a decent choice for this site. Here’s a comparison between Debian, Ubuntu and CentOS for webservers.

Changing from Apache to Nginx.

While you’re still left with the Debian support for Jessie winding down, changing to Nginx is probably a good move. Debian 9 with Nginx may well be faster than it would be with Apache. The version of Nginx you install on Debian 8 with Jessie Backports can run HTTP/2.

The problem with changing to Nginx on a live website is that it is completely different as a webserver to Apache. Nginx does not have .htaccess files and the setup will be different in ways that I’m not aware of yet. I wanted to upgrade in less than a day on this live website so this option was not right for me at the time. But, I do plan to use Nginx in the near future.

Upgrading from Debian 8 (Jessie) to Debian 9 (Stretch)

The official Debian guide to doing this manually in the command line is here. I decided against doing it this way, although it may have lead to less downtime. I decided a fresh install may cause less problems in the future.

I decided to just re-install in the hosting company portal. This meant backing everything up, then with the installation, everything was erased and had to be re-added. There is only one website on the VPS which is a WordPress blog. There was nothing particularly complicated about the server setup which made life a lot easier. The main thing I had to do were to get MariaDB installed and to import the backed up SQL file from the MySQL, I referred to this guide for that. I also had to get Lets Encrypt installed and set up the SSL encryption again.

One of the things I had to change from when I set up Debian 8 with PHP 7.0 was the way PHP 7.0 is installed/served. mpm_prefork is not compatible with HTTP/2 (mod_http2) so I had to use another way of installing PHP (from here).

I was rushing the installation through as quickly as I could because the website was down. I probably did some things in the wrong order and I did encounter some problems. However, after about 45 minutes I had to leave the house and do some chores. At that stage, the WordPress blog was basically up and running but mod_rewrite was not configured correctly yet and the SSL was not set up yet either.

When I got back I started to finish off getting the site working but almost as soon as I sat down the server locked me out of SFTP and SSH. I could not even access the shell on the control panel’s console. I actually thought I’d locked myself out with the UFW firewall. I’m not sure what the problem was but it turned out to just be temporary and I hadn’t locked myself out at all.

Everything went pretty smoothly really. I got the occasional error when trying to reboot but nothing major. Every time it was pretty obvious what the problem was without doing much, if any, investigating. The dreaded error message when Apache won’t restart is definitely becoming less scary these days since I started ignoring the files it tells you to look at and just go straight for the main error.log or whatever you have set up as the main error log file.

Learning New Stuff

I was expecting more problems from exporting the MySQL then importing it into MariaDB. Even if the import/export went smoothly I was expecting to have some issues with WordPress itself. All I can say is no problems whatsoever with the database or WordPress at all. Maybe it’s because I’ve done this kind of thing a few times before and this time I was actually ready and prepared for it.

Because the website is just a WordPress blog I am not going to be doing a huge amount of work with MariaDB on that website. But, one of the things I did take a look at was the MariaDB tuning: mysqltuner. I can’t wait to do some more work with MariaDB in future.

I did all the database backup, set up and importing in the shell. I didn’t use PHPmyAdmin at all but decided to install it at the end incase I ever needed to use it. Then, setting up PHPmyAdmin needed some more shell work with the MySQL command line and MariaDB. Because of all this, I feel like I’m a lot more confident with the MySQL command line.

The Unattended Upgrades file that came with my installation seems to be the same one used with Jessie. Referred to articles such as Unattended Upgrades in Debian 9 for what to keep and what to remove.

Unattended Upgrades will need to send emails to tell you when it needs a reboot unless you have it automatically reboot or just do not want emails. I had the Jessie server set up to send emails directly from the server. This time I decided to send the server emails with Mailgun using this guide.

With trying to get the website up-and-running normally as soon as possible I feel like I got to know Nano a lot better. I even edited the nano config file, /etc/nanorc, to tweak some of the things I have disliked about using Nano.

Issues Encountered

The Sudoers file.

After setting up a user and adding it to the group, sudo, I was getting errors every time I wanted to use a sudo command. I may have done this before but the sudoers file is located at /etc/sudoers

Something like…

root ALL=(ALL:ALL) ALL

Problem installing Python and Let’s Encrypt… dependencies.

Once I’d fixed the Sudoers file I think there may have been another obstacle to installing Python and the Lets Encrypt dependencies but I have completely forgotten what it was.

PHPmyAdmin could not create the phpmyadmin table or the ‘pma’ user.

I did not use PHPmyAdmin at all but installing it at the end caused a few problems that were easily fixed.

Outcome

All-in-all I’m very happy I got the upgrade done. And, I’m pleased I was able to do it fairly quickly despite having to leave the house against my will during the process then getting locked out. I’m looking forward to playing with and configuring Debian 9 Stretch on this VPS. And I think I chose the right way to do the upgrade. Upgrading from Debian 8 Jessie to Debian 9 Stretch on a live website through the shell can be risky and I didn’t want to get to a situation where nothing worked and I didn’t know how to fix it. The cherry on the cake is that I now have HTTP/2 working which was the main thing I wanted to add at the start, plus I have some other upgrades. Debian 9 should be ok for at least a year before I need to start thinking about upgrading again – Debian Version History.

Future

Now, having researched HTTP/2, Debian and Apache for this project I am keen to try Nginx very soon. Nginx is a similar speed as Apache for some things but is much faster in others so I am looking forward to seeing what a life without .htaccess files looks like.

Following the setting up a Linux webserver tutorial. You may also wish to create a MySQL database. First, install MySQL in the Linux console and give the root user a password. Here is what you might do for PHP 7.0 (needs sudo unless a superuser)…
apt-get install mysql-server
mysql_secure_installation
apt-get install libapache2-mod-php7.0 php7.0-mysql php7.0-curl php7.0-json

Now, from the linux command line you can login to MySQL like this…
mysql -u root -p

Now, the rest of the commands in this tutorial are run from the mysql> prompt.

After installing MySQL the main reasons you’re probably wanting to access MySQL from the command line are to create a database and create another user. To create a database…
CREATE DATABASE [database name];

To create a user, grant the user privileges on a database then reload the privileges…
CREATE USER '[username]'@'localhost' IDENTIFIED BY '[password]';
GRANT ALL PRIVILEGES ON [database name] . * TO '[username]'@'localhost';
FLUSH PRIVILEGES;

If you wanted to grant the new user privileges to everything you could do this…
GRANT ALL PRIVILEGES ON * . * TO '[username]'@'localhost';

Or just a specific table of a specific database…
GRANT ALL PRIVILEGES ON [database name] . [database table] TO '[username]'@'localhost';

This is probably all you’ll need to do in the command line. Everything else you can either do in PHPMyAdmin or in an MVC framework.

You can get a list of the databases with this command…
SHOW DATABASES;

If you wanted to manually create a table you would first “use” a database…
USE [database name];

Then, these commands are starting to look more like SQL commands you may have written in PHP…
CREATE TABLE example ( id not null auto_increment, name varchar(20) not null, constraint pk_example primary key (id) );
INSERT INTO example ( name ) VALUES ( 'Sample data' );

To import a SQL file in the Linux command line, very similar to logging in normally…
mysql -u [username] -p < [SQL file name]

To find the version of MySQL you have installed run this command...
SHOW VARIABLES LIKE "%version%";

To exit out of MySQL, some of the normal ways of exiting a command line application may not work, you'll have to use...
quit

Using a CDN is a great way to optimize your website and protect against DDoS attacks. The process of setting up a CDN is pretty simple, often changing the nameservers on your registrar is as tricky as it gets. But there can be some potential issues. Here is a short guide on how to fix some potential issues when using CDN. The example I’m using is Cloudflare but some of these issues will be the same for other similar CDNs. Some of these issues arise from changing your nameservers to a CDN, others involve Cloudflare’s Flexible SSL. All of these issues are fixable, but it is good to be aware of the issues up front.

Note: The Flexible SSL should not be used for sensitive data. For credit card transactions and personal info “Full SSL” should be used, i.e. there is an SSL certificate on the web server itself. See this guide to adding SSL encryption.

There is Cloudflare Support which contains this SSL Cloudflare troubleshooting guide for more information and also Advanced Configuration.

1) Server configuration.

If your server is misconfigured using Cloudflare’s name servers and Flexible SSL can cause problems. Obviously, if you are redirecting visitors to HTTP on the server, then you ask Cloudflare to always redirect people to HTTPS there will be problems. Solution – make sure your server is correctly configured.

2) Cloudflare’s IPs and Visitor IPs.

You may need to whitelist Cloudflare’s IP addresses in .htaccess files…

#(IPv4)
Allow from 173.245.48.0/20
Allow from 103.21.244.0/22
Allow from 103.22.200.0/22
Allow from 103.31.4.0/22
Allow from 141.101.64.0/18
Allow from 108.162.192.0/18
Allow from 190.93.240.0/20
Allow from 188.114.96.0/20
Allow from 197.234.240.0/22
Allow from 198.41.128.0/17
Allow from 162.158.0.0/15
Allow from 104.16.0.0/12
Allow from 172.64.0.0/13

#(IPv6)
Allow from 2400:cb00::/32
Allow from 2405:8100::/32
Allow from 2405:b500::/32
Allow from 2606:4700::/32
Allow from 2803:f800::/32
Allow from 2c0f:f248::/32
Allow from 2a06:98c0::/29

Cloudflare acts like a proxy, all traffic to the server appears to be coming from Cloudflare itself. If you wish to know the IP of all visitors to your website you can install mod_cloudflare to your server. There are slightly different instructions here.

3) WordPress and Cloudflare Flexible SSL.

Full SSL or Strict SSL should not need any plugin for WordPress to work normally. Simple free plugins fix the errors with the Flexible SSL and WordPress. Just search the WordPress Plugin Repository for “Cloudflare” and use the plugin that you feel will work the best.

4) PHPMyAdmin and Flexible SSL.

The potential problem is that even after you’ve specified “ForceSSL” and “Absolute URI” in the PHPMyAdmin config, PHPmyadmin can sometimes append “:80” to your domain name when you are redirected after logging in. In some versions of PHPMyAdmin (e.g., 4.1.xx), you end up at a URL which looks like https://www.website.com:80/phpmyadmin/. Using the Flexible SSL you are using port 80 on the server, but the connection between Cloudflare and the user is using port 443. This means that the addition of the “:80” causes an error and does not display PHPMyAdmin until you remove it.

The problem can be fixed in the file phpmyadmin/libraries/Config.class.php. There is a part which checks that port and whether the URL is HTTP or HTTPS.

A solution is from here, you find the following code and fix this by commenting out the last OR condition:

if (! empty($url['port'])
&& (($url['scheme'] == 'http' && $url['port'] != 80)
|| ($url['scheme'] == 'https' && $url['port'] != 80)
//|| ($url['scheme'] == 'https' && $url['port'] != 443)
)) {
$pma_absolute_uri .= ':' . $url['port'];
}

It would be better to use Full SSL for PHPMyAdmin but this is a solution to the problem if you have to use Flexible SSL.

5) Full/Strict SSL or Flexible SSL.

When you first add Cloudflare to your website it will fetch the settings and it will decide whether to use Full SSL if the website has it’s own SSL certificate or Flexible SSL if it does not. If you want to change from Flexible to Full or vice versa after setup, make sure that the website is using the correct setting (Full or Flexible) in the “Crypto” tab.

Basic Steps After Installing Laravel 5.4

You have successfully installed Laravel 5.4 and you have directed the domain name at the public directory and you can see then Laravel test page. What next? Assuming that you are going to be using a database to run your website, here are some basic steps in how to build a Laravel website from the Laravel test page.

For this simple example, this guide may work for Laravel 5.x but this guide is specifically for Laravel 5.4.

Contents

Useful Commands
1) The Database
2) The .env file
3) The Model
4) The Migration
5) The Route
6) The Controller
7) The View
Conclusion
Errors and Solutions

Useful Commands

If you want to clear Composer cache (-o flag means optimized)… composer dump-autoload -o

Die and dump, like var_dump($variable); would be this in Laravel… dd($variable);

List all Artisan commands help… php artisan list then look at the help file for a specific command php artisan help controller:make

Run tinker… php artisan tinker. Then, exit with “q” or CTRL-C.

1) The Database

Create the database and give it a user.

2) The .env file

Add the database login info to the .env file which should be in your root (the level below “public“).

While you’re there you may aswell update as much of the info in the .env file as you can. If the name of the website has a space in it remember to use quotes. Not using quotes will cause an error.

You can also update the config/app.php file. If you get errors after updating this file double-check the permissions and ownerships and you may need to run composer dump-autoload -o afterwards if errors persist.

3) The Model

Laravel is an MVC framework. It is designed to use Models, Controllers and Views.

In this simple example, we’re just going to list some data from a database onto the page. The data we want to display are some names. One way to make a Controller for “names” is using Artisan like this… php artisan make:controller NameController. You can make the Model and the Controller separately, but we also need a Migration, why not make all three together in Artisan like this…

php artisan make:model Name -mc

The -mc literally means to create a Migration and a Controller at the same time as the Model.

If the Model is Name (capitalized and singular), the Controller would be NameController (camelcase), the Migration would be create_names_table, and the resulting table would be names (lowercase, plural). The Models are in the app directory.

Using this method the Model is already using Laravel’s database engine, Eloquent, and because of the naming convention, the Model knows which database it is connected to so we do not have to add it manually.

The Model can contain relationships and scopes, but in this simple example, we will not alter it at all.

4) The Migration

The Migration files are created for you in Step 3, they will be in the database/migrations directory. Now, you have to modify them for the data you want to use. I only want each name to have an ID and the name itself in this example, so I can modify the Migration to look like this using Laravel’s Schema…

public function up()
{
Schema::create('names', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
});
}

Here, id is an incremental integer and name is a string. For more database column types you can use in Migrations see Adding Columns. You can also specify indexes, foreign keys and set a default value for a column in the Database: Migrations.

Then, php artisan migrate builds the info from the Migration tables in the database.

If you change the migrations you can rollback and then make the migrations again with

php artisan migrate:refresh

5) The Route

Once you have a Controller and Model you can make a simple route. The routes directory has a file called “web.php”…

Route::get('/', '[email protected]');

'/' means when the URL is the root domain Laravel will call the Controller and method in the second parameter.

NameController is the name of the Controller that was created in Step 3.

index is the method we want to use. There are up to 7 default behaviours that a Controller should have: index, create, store, show, edit, update, and destroy. These methods within a Controller are called actions. If you find that your controller needs more than these 7 actions it may be a good idea to create a new Controller.

6) The Controller

The Controller is the file “NameController.php” that lives in the app/Http/Controllers directory. From Step 5, we said that we wanted to use the index method because we just want to list all the names. To do this we can use Eloquent and the all() function to simply grab all the rows from the table…

use App\Name;
public function index ()
{
$names= Name::all();
return view ( 'names.index', compact('names') );
}

If you “use” the Model in this way at the top, use App\Name;, you can refer to the Model in the methods like this Name::all(); as opposed to App/Name::all();. Instead of using all() you could also use Eloquent but order the results differently using get()

$names = Name::orderBy('name', 'desc')->get();

The same thing as all() but not using Eloquent would be…

$names= \DB::table('names')->get();

The \ is needed in front of the DB because you are inside the Controller and the DB class exists outside of it. Or, just “use” DB at the top of the controller, see Queries.

Out of these two different ways to interact with the database, it is better to use Eloquent and to keep most of the database-related logic inside the Model. In more complex projects than this example, the Model would contain Scopes and Statics that can be referred to from other places, such as Controllers.

The 'names.index' is the View we want to use and compact('names') is a way to pass the $names array into the View, alternative ways are listed in Passing Data to Views, for example…

return view('names.index')->with('name', $name);

7) The View

In the Controller we said the View we wanted to use was names.index. This means that Laravel will be looking in the resources/views/names/ directory for the file: “index.blade.php“. So make this file.

The View uses the Blade templating engine so ideally, the contents will be a mixture of HTML and simple logic. You can either use normal PHP, like <?php if($names){ ?>. Or, it may be better to use the blade templating system and write it using Blade like this @if ($names). With Blade you do not have to keep opening and closing the PHP making it a lot easier to write and cleaner to look at than normal PHP mixed into the HTML would be.

We want to display all our names on the page using Blade. In step 4, we gave the table 2 columns in the migration, “id” and “name”, and, in step 6, we got all the data from the “names” database. Now, they can be displayed very simply in a list like this within HTML using Blade…

@if ($names)
<ul>
@foreach ($names as $name)
<li>{{ $name->id }} - {{ $name->name }}</li>
@endforeach
</ul>
@endif

Now, when you go to the root domain of the Laravel website the Route sends you to the correct method of the Controller, which uses the Model and the View to grab the names and display them (assuming you have also inserted some rows to the database).

Conclusion

The 7 steps of this very simple beginners tutorial use the route, model, controller and view parts of Laravel.

  • The Route links the URL with a specific control and method, and can pass variables through to them.
  • The Model just links the table in the database to the controller in this simple example. In more complex examples the Model interacts with the database more and specifies relationships.
  • The Controller does what we want the page to do, in this case, it is to fetch the list of names from the Model (database) and it also says which view will display the data.
  • The View takes the data from the Controller and displays it on the page.

Errors and Solutions

An Artisan command produces an error like this…
[Illuminate\Database\QueryException]
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table users add unique users_email_unique(email))

Edit your “AppServiceProvider.php” file in the app/Providers directory and add to the boot method a default string length. You also have to make sure to “use” the Schema at the top.

use Illuminate\Support\Facades\Schema;
function boot()
{
Schema::defaultStringLength(191);
}

****************************
If migrate:refresh produces an error like…

[Illuminate\Database\QueryException]
SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'users' alre
ady exists...

Manually drop the tables in tinker by running tinker (see Useful Commands) and running the command…
Schema::drop('users')

Now, you can now run php artisan migrate to migrate all the tables.

****************************
Pages aren’t loading at all (fatal error), looking at the logs gives the error message…
Fatal error: Uncaught exception 'UnexpectedValueException' with message 'The stream or file...

Solution, make the Laravel log files writable…
sudo chmod -R 777 storage/logs

What do you do when the Start button stops working on Windows 10? Here is the solution from here

1. Launch Task manager
Press the [Ctrl] + [Alt] + [Del] keys on your keyboard at the same time – alternatively, right click the Taskbar. Then select Task manager.

2. Run a new Windows task
When the Task manager window opens, click the More details option to expand it, then select Run new task from the File menu.

3. Run Windows PowerShell
When the Run new task dialog box opens, type powershell, tick the box for Create this task with administrative privileges and click OK.

4a. Run the System File Checker
Type sfc /scannow into the window and press the [Return] key.

4b. Reinstall Windows apps
Launch the Task manager and open a new PowerShell window with administrative privileges, as explained above. When the Windows PowerShell window opens copy the line below and paste it into the PowerShell window by simply right-clicking at the blinking PS C:\WINDOWS\system32> prompt, or by pressing [Ctrl] + [V] on the keyboard:

Get-AppXPackage -AllUsers | Foreach {Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml"}

Wait until the app download and installation process completes — ignore any red text that appears — then restart Windows.

I was unable to find a single tutorial that would show exactly how to install Laravel on Debian, possibly because I was using PHP 7.0 on Debian, which no other examples seemed to use. As such, I had to take bits and pieces from different websites to make this tutorial on installing Laravel on Debian. The same tutorial will also work for Ubuntu but I found that on Ubuntu 16.04 a lot of the dependencies were already installed out of the box, Debian may require more care to make sure everything is present. Once you have all the requirements installed it should be easy to install composer, and once composer is installed Laravel should also be fairly simple if you are doing the right things.

I found that some methods of installing Laravel did not work on my setup, this may have been because it was Debian or because I’d missed a requirement. This is the method that worked for me, and also works on Ubuntu.

Installing Composer

Before starting the most important thing is to make sure to apt-get upgrade and apt-get update… and also, make sure all the Laravel server requirements are installed. Once that’s done, restart/reload and begin. From here, you can install composer by typing these commands into the Linux terminal…

curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer

Installing Laravel

Then navigate to where you want to put the new website and create a new project. The name of the project will be the name of the directory, so here I’ve called it example.com, because it is a shared server…

cd /var/www
sudo composer create-project laravel/laravel example.com --prefer-dist

This way you can easily specify the directory name. There is an alternative method here.

After installation, the last thing it should tell you is what the encryption key is. A good thing to do right away would be to check that this is the same as the value in the .env file. If the installation was successful it should be there, but it is good to check and also to have a first encounter with the .env file!

Configuring your Website to Run Laravel

Now, set up this new domain as normal on your webserver, making sure that instead of pointing to the /var/www/example.com directory, you point to /var/www/example.com/public.

Next, one thing that I was not prepared for with Laravel was the need for the ownerships and permissions to be correct. All the files will probably be owned by the root user, so change them to whichever user they should be (i.e. not root). So, navigate to your web directory and change the owner as follows…

cd /var/www
sudo chown -R youruser example.com

Then, after that navigate to the public folder in your directory and it may not work. First make sure the storage directory has the correct owner, group and permissions…

cd /var/www/example.com
sudo chown -R youruser:www-data storage
sudo chmod -R ug+rwx storage

Changing storage may fix the problem, or if there are still problems you can also do this for the bootstrap/cache directory.

Now, make sure your site is pointing at the public directory and it should be working now and showing you the Laravel welcome page…

Laravel Welcome Page

If you see a page like this in your browser in the public directory, Laravel was installed and is working.

If not check the requirements are installed, then the permissions, owners, and groups. In particular, if it is a brand new web server with no other websites already running on it, check things like MySQL and all the requirements for both Composer and Laravel.

Finishing Up

From this stage, if you see the Laravel welcome page you’re all set up. You can set up a database and a database user in MySQL and enter the information in the .env file. You should also make sure that mod_rewrite is installed and that your Virtual host config file is allowing it to work.

Troubleshooting

If you run the composer create-project command but you get an error, maybe you have some missing dependencies, the files will be in the new directory but nothing will be installed yet. To install you’ll need to install the dependencies then run the command composer update laravel which will complete the installation. I got this error on Ubuntu 16.04 because PHPUnit was not installed.

If after following this guide you get an error like… No application encryption key has been specified. Run this command to generate a key (automatically adds it to the .ENV file)… php artisan key:generate

The internet is gradually becoming more and more encrypted and secure. While this is most important for websites where you might enter sensitive data, like credit card info, it is generally a good thing to have for any website. Browsers now warn surfers if a website is not encrypted. As I write this unsecure, unencrypted websites just have an exclamation mark inside a circle in the location bar, but the trend is towards this warning getting more and more visible on the surfers browser.

As well as having quality content for surfers, you can also try to not turn surfers off your content by having an unencrypted website. Maybe secure websites will rank higher than unsecured ones.

Contents

Setting up a SSL Certificate to make a Secure Website

When trying to set up a free SSL certificate from Let’s Encrypt the information I found to begin with was quite confusing. Then, once I got my head around what I was doing, the sites I tried to add it to were not able to use Let’s Encrypt certificates for different reasons.

Once I finally got a VPS that could handle Let’s Encrypt certificates I followed this guide… Install Let’s Encrypt to Create SSL Certificates.

I would say that starting off it is important to pay attention to detail. Make sure everything is updated to begin with, and you have all the server requirements. Also, whereas you may not have specified the IP address in your virtual host config file before, you will need to specify the IP there for this to work.

Luckily, for a Linux administration beginner there are plenty of error logs and hints to find out what if anything has gone wrong. Not all the online guides state all the pitfalls. That’s why I am putting this information together in one place on how to make your website secure with SSL encryption…

Installing Let’s Encrypt

To begin with, make sure port 443 is open in the firewall (see troubleshooting). In these lines of code make sure to change “example.com” to your website domain.

Start off by getting and installing the Let’s Encrypt software, then use it to get the certificates. The order of the two domains in the final command are fairly important because the virtual directory containing the certificates will be named after the first domain, so you might want to use the one without www for simplicity…

sudo apt-get install git
sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt

Installing Let’s Encrypt SSL Certificates

Go to the directory you have cloned to when installing Let’s Encrypt…

cd /opt/letsencrypt
sudo -H ./letsencrypt-auto certonly --apache -d example.com -d www.example.com

If there are any errors after the final command, it should tell you what the error is related to, but if it does not or it is unclear there are some ideas in the troubleshooting section.

Check the certificates by typing the following commands, below. The directory should be your domain instead of “example.com“, if it does not exist or is misspelt then you’ll have to get the certificates again as it will not work. When you get the certificates you should specify both www and non-www. Let’s Encrypt does not work for wildcards so every subdomain has to be specified. If it is a subdomain with a different config file then it will need a separate set of certificates…

sudo ls /etc/letsencrypt/live/example.com
sudo stat /etc/letsencrypt/live/example.com/fullchain.pem

Modify the VirtualHost file to look for SSL Certificates

Then, once you have the certificates. Modify the config file for the website, e.g. “/etc/apache2/sites-available/example.com.conf“. You can Keep the same file just change the port from 80 to 443 and the VirtualHost tags should contain the following. The file path to the certificates should be the actual paths to your certificates. And, I believe for this to work you must have the IP address in the VirtualHost tag, a wildcard will not work…

<VirtualHost 123.123.123.123:443>
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
</VirtualHost>

Then, make sure SSL is enabled and restart apache…

a2enmod ssl
service apache2 restart

By this stage when you go to https://www.example.com your website should work! If that’s the case you can make sure that everyone gets the secure version of your site, below.

Redirecting to Force HTTPS Encryption

Once the SSL certificates are working properly you’ll want to make sure everyone gets the secure version of your website. There are a few different methods to do this but perhaps the simplest for people who are new to Linux is using .htaccess. How to force HTTPS using the .htaccess file

RewriteEngine On
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://www.example.com/$1 [R,L]

This will still allow https://example.com/ to work so you may choose to add another line for that.

Or, have two VirtualHost tags for ports 80 and 443 in the “/etc/apache2/sites-available/example.com.conf” file and redirect port 80 as suggested here

<VirtualHost 123.123.123.123:80>
ServerName www.example.com
ServerAlias example.com
Redirect permanent / https://www.example.com/
</VirtualHost>

Now, you’ll all done!

Checking the Expiration Date of Let’s Encrypt Certificates

Let’s Encrypt certificates last for 90 days, you do not want them to expire unless you want to stop encrypting the website. To check when your current certificates are du to expire type this into the terminal…

openssl x509 -noout -dates -in /etc/letsencrypt/live/example.com/cert.pem

Which will give you the date they were created and the date they are valid until…

notBefore=May 7 14:33:00 2017 GMT
notAfter=Aug 5 14:33:00 2017 GMT

From here there is also “ssl-cert-check” for Debian-like versions of Linux. It needs installing first if it is not already installed, so…

apt-get install ssl-cert-check

Then, run it by typing this simple code (needs superuser privileges so use “sudo”)…

ssl-cert-check -c /etc/letsencrypt/live/example.com/cert.pem

Which gives something like this…

Host                                             Status   Expires     Days
---------------------------------------------------------------------------
FILE:/etc/letsencrypt/live/example.com/cert.pem   Valid   Aug 5 2017   90

Renewing Let’s Encrypt SSL Certificates

To renew the Let’s Encrypt certificates, navigate to the Let’s Encrypt directory… cd /opt/letsencrypt.

Then, this is one way to renew the certificates…

sudo service apache2 stop
sudo -H ./letsencrypt-auto certonly --standalone --renew-by-default -d example.com -d www.example.com
sudo service apache2 restart

The “standalone” flag means that you would have to stop apache because Let’s Encrypt needs to be the only application using port 443.

However, by using --apache or --webroot you can do the same thing while apache is running, from here. This may reload or restart apache but apache would not be stopped for nearly as long as it would be if apache was manually stopped and then restarted again at the end…

sudo -H ./letsencrypt-auto certonly --apache --renew-by-default -d example.com -d www.example.com

To automatically renew the Let’s Encrypt certificates one a month, enter this command to make a monthly task in your crontab…

echo '@monthly root /opt/letsencrypt/letsencrypt-auto certonly --quiet --apache --renew-by-default -d example.com -d www.example.com >> /var/log/letsencrypt/letsencrypt-auto-update.log' | sudo tee --append /etc/crontab

To automatically renew the Let’s Encrypt software, enter this command…

echo '@monthly root cd /opt/letsencrypt && git pull >> /var/log/letsencrypt/letsencrypt-auto-update.log' | sudo tee --append /etc/crontab

Alternative Method for Debian 8 (Certbot)

Another way is to install the Let’s Encrypt certificates is to install Certbot on Debian 8 using Backports… How to Set Up Let’s Encrypt Certificates for Multiple Apache Virtual Hosts on Ubuntu 14.04.

sudo nano /etc/apt/sources.list

Then, enter this line if it is not already there…

deb http://ftp.debian.org/debian jessie-backports main

Save and exit nano, then…

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install python-certbot-apache -t jessie-backports

Troubleshooting and Checking

Check which sockets are open using ss (socket statistics)…

ss -tlnp

More information on ss here.

Check apache version and check which modules are installed

apache2 -v
apache2ctl -M

or

dpkg-query -l

For your second secure website on the same webserver you will have already enabled the SSL module and port 443 will already be serving your first website to the world. When you try to install the certificate for the second website there may be problems if you are using the --standalone flag, because Let’s Encrypt wants to use the same port as is already serving the website. To continue using --standalone you can stop Apache for a few seconds while you install the certificates.

service apache2 stop

Then, once the Let’s Encrypt certificates are done, you can restart Apache again…

service apache2 restart

To avoid having to stop and start apache like this on a production website you can use the --apache flag instead.

If you are using UFW as a firewall, the following command will show you whether socket 443 is allowed…

sudo ufw status

Also, Apache problems when trying to set up SSL (Debian).

Further Checks to make your Website Secure (Green Padlock)

If your website is working with HTTPS in the location bar (“https://www.example.com/”) but you do not have a green “SECURE” you have an “i” with a circle around it, check to see what it says by clicking on it. If it says you are not fully secure it probably means that the files and/or images you are linking to are not using the SSL encrytion. To fix this simply either fix your absolute image/file locations by changing them from HTTP to HTTPS, or just use relative linking.

Another thing that may cause errors is your .htaccess file, if you have one. Make sure that the error documents are all updated from http:// to https:// for the site in qustion.

Adding more Secure websites to the same Webserver

When “Let’s Encrypt” is already installed you do not have to re-install it, you can simply create the new certificates for the next website you want to make secure. When you are trying to create the certificates there may be an error saying that port 443 is already in use. In this case, you may have to stop apache briefly while you make the certificates, then restart apache as soon as they’re made.

Here is a guide to Setting up a Linux Webserver with SSH. This is for Debian 8 (Jessie) although other versions of Debian will be similar, and Debian-based versions of Linux, like Ubuntu will also use similar commands. I’ll just call it the webserver because it can be a dedicated server, a VPS, or a cloud instance.

A lot of this comes from these excellent guides… Getting Started with Linode and Securing your Server… but I’d say that searching around and getting different information from different sources is definitely a good way to go. I wanted to install PHP 7.0 so some of this guide is different to other guides, which tend to use PHP 5.6. This guide is also primarily for people using a Windows PC to talk to a remote Linux webserver, but other operating systems will be the same for most of the stages. Most of the contents of this guide uses a terminal and an SSH connection, which windows needs software for (e.g. Putty), but other operating systems may have it built-in. This guide doesn’t talk about FTPing (or SFTPing) much, but a good SFTP client for Windows is WinSCP.

There is often more than one way to get the same results. For example, you can reboot the system in a number of different ways. I tend to use the methods that appear simplest to me.

Requirements

  • A linux server (Debian 8, in this case).
  • The IP address of the server.
  • The root password of the server.
  • A computer to access your remote linux webserver, in this case a Windows PC with Putty installed.

Contents

1) Login to the server with SSH
2) Update/upgrade
3) Install Apache2
4) Find your webserver’s IP address
5) Set a Hostname
6) Configure the Apache virtual hosts
7) Create the web directory
8) Install PHP7
9) Your webserver’s Timezone
10) Unattended upgrades
11) Install Sudo
12) Add a user
13) Public/private key pair
14) Turn off root login
15) Sending email
16) Fail2ban
17) Firewall: UFW
18) Install MySQL and PHPmyAdmin
19) Monitor
20) Apache Config file
Conclusion

1) Login to the server with SSH

With Windows you can download and install some software, such as Putty, then connecting to your instance should be pretty straight-forward with the login information from your hosting company. To begin with you’ll probably be logging in as “root” so you will not need to type sudo as root is the master login (this is handy because sudo is not installed, yet). The commands on this guide may need sudo adding at the beginning if you are not logging in as “root”.

2) Update/upgrade

Once you are logged in the first thing to do is to make sure everything is updated so that you are installing the latest versions of everything when you get to that stage.

apt-get update && apt-get upgrade

or, later if you login as another user, you’ll need sudo…

sudo apt-get update && sudo apt-get upgrade

3) Install Apache2

First, you can check if Apache is already installed by typing…

which apache2

If it is not already installed, i.e. the above does not give you any results. To install Apache type…

apt-get install apache2

4) Find your webserver’s IP address

If your hosting company has not told you what the IP address is, this shows all IP’s installed on your instance…

hostname -I

5) Set a Hostname

The hostname can be anything. If you have more than one instance you might name your instances so that they are all plant species, stars, geographical locations, chemical elements, Roman Emperors or Greek philosophers.

Add the hostname by opening up the hosts file in nano…

nano /etc/hosts

Then, the hosts file might look like using the IP address that you already know, above…


127.0.0.1 myhostnamegoeshere.example.com myhostnamegoeshere localhost
127.0.1.1 myhostnamegoeshere
123.123.456.789 example.com

Also, edit the hostname file…

nano /etc/hostname

Then, after this you’ll need to reboot the instance from the control panel of the hosting company.

The hostname and FQDN are important for all kinds of server functions. Test your hostname settings like this…

hostname hostname
hostname -i ip address
hostname -f or hostname --fqdn fully qualified domain name

6) Configure the Apache Virtual Hosts

Make sure 000-default.conf is off, copy it to mywebsite.com.conf and enable. link

cd /etc/apache2/sites-available
cp 000-default.conf example.com.conf
nano example.com.conf

Then, edit the example.com.conf so that it looks like this…

ServerAdmin [email protected]
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com

Then, enable the site by typing…

a2ensite example.com

7) Create the web directory

We specified the DocumentRoot should be /var/www/example.com, above. So, now we need to make it!

Move to the place for all your web directories…

cd /var/www/

Then, create the web directory…

mkdir example.com

Once you have the directory you can add an index.html inside it.

If you have not restarted apache2 since typing the a2ensite command you need to do that before the domain name will work in your browser. Restart then test it by typingthe domain name into a browser.

8) Install PHP7

You can install any version of PHP. For Debian 8, it seems that PHP5.6 is the default, whereas with Ubuntu 16.04 LTS PHP7.0 is the default. I wanted to go with PHP7.0 so followed this guide for setting it up on Debian 8… Installing PHP7

Add these two lines to your /etc/apt/sources.list file:

deb http://packages.dotdeb.org jessie all
deb-src http://packages.dotdeb.org jessie all

Add the GPG key:

wget https://www.dotdeb.org/dotdeb.gpg
apt-key add dotdeb.gpg

Install PHP 7:

apt-get update
apt-get install php7.0

Then, straight away before you do too much else you’ll want to add the other modules you need. There is information abput some common ones from here..

apt-get install php7.0-cli php7.0-common libapache2-mod-php7.0 php7.0 php7.0-mysql php7.0-fpm php7.0-curl php7.0-gd php7.0-bz2

You can then test out the PHP by making a page that has phpinfo(); on it to show you 1) that it’s working and 2) the modules that are installed and enabled. Assuming everything works fine it’s time to do more setting up…

At this stage I normally make sure everything is installed that I’ll need for phpmyadmin. Then enable mod_rewrite

a2enmod rewrite

9) Your webserver’s Timezone

Set your server timezone by typing the following command…

dpkg-reconfigure tzdata

Then, this command shows you the current time on your server…

date

10) Unattended upgrades

You will want to keep everything as up-to-date as possible on your instance. You can do this by configuring the Unattended upgrades… Automatic updates. Then if you don’t want to have to login to your instance and reboot/restart it after every upgrade, you can automatically reboot, if needed, after upgrading. First install the package…

apt-get install unattended-upgrades

Then configure the settings you want in nano…

nano /etc/apt/apt.conf.d/50unattended-upgrades

11) Install sudo

From Installing sudo on Debian

apt-get install sudo

12) Add a user

adduser myname

Then, add the user to sudo…

usermod -aG sudo myname

Then logout of root, login as myname and test sudo out by doing something that requires sudo privileges, e.g. reload apache2.

13) Public/private key pair

This is the main thing that is different on Windows PCs than it is with either Linux or Macs. For Windows PCs you have to use downloadable software, such as putty. This guide: Use Public Key Authentication with SSH talks you through the process.

The most important thing to note is that you should be logged in as the user you want to make the key pair for when you make the ~/.ssh directory and put the authorized_keys file in it, the reason is that the “~/” means “/home/yourusername/”, so if you login as root when you make the directory it’ll make the file in “/home/root/” which will not work. You should also make the file owned by your username… chown yourusername ~/.ssh -R as suggested here.

Then, logout of root and login with your user to check that the new user works correctly with the public key authentication. You should be asked for the keyphrase, as opposed to the password.

14) Turn off root login

If you can login with the username you set up with the public/private key pair you can “turn off” root logins.

Turn off root logins and turn off password authentication in the sshd_config file using nano…

nano /etc/ssh/sshd_config

For the changes you have made to work, you will need to restart SSH…

service ssh restart

15) Sending email

https://www.debian.org/releases/jessie/amd64/ch08s05.html.en allows server to send email, e.g. for unattended upgrades reboot.

16) Fail2ban

Fail2ban

apt-get install fail2ban

Then, you can edit the config file, and/or make a local file…

nano /etc/fail2ban/fail2ban.conf

17) Firewall

For a firewall, UFW (Uncomplicated Firewall) is there for Debian systems. This is a good guide: Configure firewall with UFW

apt-get install ufw

As the guide says, you’ll have to be very careful when switching on the firewall that you do not lock yourself out of SSH.

18) Install MySQL and PHPmyAdmin

Here are two useful guides: MySQL and MySQL on PHP7.

apt-get install mysql-server
mysql_secure_installation
apt-get install libapache2-mod-php7.0 php7.0-mysql php7.0-curl php7.0-json

Then, once MySQL is installed there is more MySQL information here about how to create a database and a new user.

A good place to begin before installing PHPmyAdmin is by checking the phpmyadmin prerequisites and installing everything needed (e.g. mbstring). If 500 server errors, check the prerequisites again, installing anything that isn’t already installed, then re-install phpmyadmin to get rid of the error. Install PHPmyAdmin on Debian 8

sudo apt-get install phpmyadmin

For each virtual host that you would like to give access to your PHPMyAdmin installation, create a symbolic link from the document root to the phpMyAdmin installation location (/usr/share/phpmyadmin). You do this by moving into the public directory of each virtual host, then creating a symlink…

cd /var/www/example.com/public_html
sudo ln -s /usr/share/phpmyadmin

19) Monitor

You can monitor the performance and usage of the web server by using top… Just type top into the terminal and you’ll get a summary of the usage every few seconds.

Linux TOP command explained

You can also set up a script that will email you if certain conditions are met (RAM usage, for example).

20) Apache Config file

It’s a good thing to take a look at the apache2 config file to make sure it is configured correctly. One of the things you’ll want to check is that KeepAlive is set to off. From Setting up LAMP on Debian 8.

sudo nano /etc/apache2/apache2.conf
KeepAlive Off

TL;DR / Conclusion

Different setups are all slightly different. If you are getting errors the best thing to do is to either examine the error itself or find the relevant error logs and do some problem solving. Unix & Linux Stack Exchange and Server Fault tend to have a lot of problems already answered.

This is just a general guide to setting up a Linux Webserver with SSH with a Windows PC talking to a Debian 8 remote webserver. You may want to change the order I have followed here and there may well be steps I’ve missed out here, errors I get that you do not get, or vice versa. But, I hope it can be useful to someone.

After getting this far there will probably still be thing you need to do to your webserver. There will be other PHP and Apache modules that your web applications and/or frameworks require, such as mod_rewrite… As before, figure out what you need for your application and add it as required. Have fun!

There are 1,000s of WordPress Plugins and each one is individual, but for anyone wanting to make a plugin for themselves here are a few basics to help you get stated. I made my first plugin recently for WordPress and I was amazed at how little information was out there. So, I decided to make this simple tutorial on “How to write a WordPress Plugin” for anyone else with an interest in web programming or web design who would like to see if they can make a plugin. Obviously, the plugin itself will be whatever you want it to be and will be as complex as you want to make it. This tutorial will be an easy introduction that you can add to if you wish.

Prerequisites

While it can be very straight-forward there are two things you’ll definitely need to begin working on a WordPress plugin.

  • A working version of WordPress wether on a website or on a localhost.
  • Knowledge of PHP.

You are going to need a working version of WordPress. I won’t go into the details here of how to setup and install a WordPress blog but if you are interested in making a WordPress plugin I’m assuming that you’ve had some experience with using WordPress.

WordPress has many existing functions that will assist you in making your plugin but the programming language that you’ll need to use to make a WordPress plugin is PHP. There’s no getting around the fact that you should probably have some PHP knowledge to make a plugin that behaves exactly the way you’d like it to behave and has all the functionality you want it to have. I’d say that you may not have to be a PHP whizz and you can learn-as-you-go, but you’d need to have an understanding of programming languages, and an idea of how to write PHP.

A good source of information about PHP is PHP.net, often searching for a PHP function will take you there. Another good place for PHP examples and troubleshooting is Stack Overflow, again searching for a problem you are having will often take you here where someone has generally already asked the question and it has already been answered.

Why WordPress? Why a plugin?

WordPress rocks. There are many many people using WordPress all over the world, and WordPress.com is in the world’s top 50 websites. The basic blogging software is amazing and constantly evolving, but the power of WordPress is in it’s open source. Because the source code can be viewed by anyone and there is extensive online documentation many people have made plugins and themes which aid and enhance WordPress blogs. Through plugins you can modify almost any aspect of the blog, adding things, removing things and changing it until it is almost unrecognisable from the original blog you added to your hosting.

You’ll want to use plugins that other people have made, but maybe the plugin that would be perfect for you has not been made yet. If this is the case or you just want to give it a try, read on…

Making a WordPress Plugin: First Things First

Ok then, let’s begin! We’ll start off with a one page plugin. The first thing you have to do is open up a text editor and create a file called whatever you’d like your plugin to be called. There are 1000’s of plugins already so my advice would be to call it something with at least 2 or 3 words in the name if you wish to submit to the WordPress Repository. WordPress needs each plugin to have a unique filename so that there are no conflicts between different plugins. This concept extends to the naming of functions which should all begin with a unique prefix so as not to cause any conflicts with WordPress functions or functions of other plugins. If you are not interested in submitting your plugin you can call it pretty much whatever you like as it’ll only be on your website.

So, let’s call our plugin “My First Ever Plugin” and give it the filename “my-first-ever-plugin.php” and let’s start off with this code in our PHP file, I’ll call this the “header” code…

<?php
/**
 * @package my-first-ever-plugin
 * @version 0.0.01
 * Plugin Name: My First Ever Plugin
 * Plugin URI: http://www.shortdark.net/my-first-ever-plugin/
 * Description: My plugin does...
 * Author: My Name
 * Text Domain: my-first-ever-plugin
 * Version: 0.0.01
 * Author URI: http://www.shortdark.net/
 */

It should be fairly self-explanatory. This plugin will have the file "my-first-ever-plugin.php" in a directory called "my-first-ever-plugin". If your plugin was called "Whatever" it would have a file called "whatever.php" inside a directory called "whatever". The description is a short description of what the plugin does.

Prevent Direct Access

Next, still on the "my-first-ever-plugin.php" file, it's good practice to prevent direct access to your plugin. I.e. we want the plugin to be used by someone within their WordPress blog, we do not want the plugin to be accessed from elsewhere. To prevent direct access we insert this code after the header...

/**************************
 ** PREVENT DIRECT ACCESS
 **************************/

defined('ABSPATH') or die('No script kiddies please!');

So, if "ABSPATH" is not defined the PHP script will die().

Believe it or not, we're almost done if you're just making a WordPress plugin for your own use. I'll cover the steps you need to take if you want to add your plugin to the WordPress repository later. All that has to be done now is to hook some code to WordPress.

Hook It Up

WordPress uses "hooks" to connect functions you have written to a WordPress blog. A hook can be either an "action" or a "filter". An action adds something and a filter changes something that already exists. We will use a filter for this tutorial but an example of an action would be to make a page in the admin area we would use the "add_action" like this... "add_action('admin_menu', 'function_name');". For more information check out the WordPress Plugin API reference.

Here we are filtering the content of all the posts and pages. We are going to add some "Hello World!" text in <p> tags at the end of the post and page content. We make a function that has the parameter "$content" then we hook the function using the filter "the_content" whoch means that whatever we return from the function will replace the post or page content. As we do not want to remove the content we just want to add to it I have just added the string we want onto the end of the "$content" variable.

This goes next in the "my-first-ever-plugin.php" file...

/**********************
 ** DO SOMETHING
 **********************/
// Add "<p>Hello World!</p>" to the end of each post and page
function mfep_hello_world($content) {
	$content .= "<p>Hello World!</p>";
	return $content;
}

// Hook the function to "the_content"...
add_filter('the_content', 'mfep_hello_world');

Notice, I have named my function beginning with an acronym of my plugin. My plugin is called "My First Ever Plugin" so I put "mfep_" at the start of all my function names.

And, that's it. You've made a plugin!

Make your WordPress Plugin Translatable

To make your plugin translatable you'll need to load your text domain. In the "header" we had a line like this...

 * Text Domain: my-first-ever-plugin

So, now we need to use another hook, which this time is an "add_action" to tell WordPress where you'll be storing the translation files. Here, I'm storing the translation files in a directory within my plugin called "languages".

/****************************
 ** LOAD PLUGIN TEXT DOMAIN
 ****************************/

function mfep_load_textdomain() {
	load_plugin_textdomain('my-first-ever-plugin', false, dirname(plugin_basename(__FILE__)) . '/languages');
}

add_action('init', 'mfep_load_textdomain');

Again, you'll notice that my function has the acronym of my plugin name, "mfep_", at the beginning.

That's all we have to do in the "my-first-ever-plugin.php" file.

readme.txt

If you want to submit your plugin to the WordPress repository you'll also need a "readme.txt" file in your plugin directory.

Similar to the header of the PHP file, the readme.txt also has a header...

=== My First Ever Plugin ===
Contributors: shortdark
Donate link: http://www.shortdark.net/my-first-ever-plugin/
Tags: posts, pages
Requires at least: 3.5
Tested up to: 4.6.1
Stable tag: trunk
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html

Adds a Hello World! to the end of the post and page content.

The plugin title is on the top line in between two sets of "===".
Contributors is hwo has helped to make the plugin.
Tags should be exactly the same as the ones that WordPress has already. You can see the existing tags here.
Requires at least is the minimum WordPress version number your plugin should work for. Our plugin here is very simple but if you used a WordPress function that was created fairly recently you should investigate which version of WordPress it was introduced in. This will allow you to calculate the minimum version without installing every single version of WordPress ever.
Tested up to should be the version of WordPress you have tested your plugin on. This should always be the latest version of WordPress because you should always update to the latest version every time an update comes out.
Stable tag is telling WordPress which is the stable version of your plugin. To begin with this will be the "trunk".
Then, your plugin must be open source and it must have a public licence,
Lastly, before the next section of the "readme.txt" you can add one sentence to describe your plugin. This will be what will show up on the plugin page of the user's blog after they download your plugin.

The rest of readme.txt

After the one sentence short description you can add a much longer description that can be as long as you like with several paragraphs. Links can be added as shown with the link text in square brackets before the URL in brackets. Then comes the necessary "Installation" and "Changelog" sections. You'll notice that all the sub headings are in the middle of a set of "==" and the subheadings have "=" around them.

== Description ==

Much longer description goes here.

Please let me know if you like this plugin by leaving a review or [contacting me](http://www.shortdark.net/contact-me/).

Go to the [Shortdark WordPress plugin page](http://www.shortdark.net/wordpress-plugin/) for more information.

== Installation ==

This section describes how to install the plugin and get it working.

1. Upload the plugin folder to the `/wp-content/plugins/` directory, or install the plugin through the WordPress 
plugins screen directly.
2. Activate the plugin through the 'Plugins' screen in WordPress.

== Changelog ==

= 0.0.01 =

* New plugin.

And that's it!

Once you've finished your readme.txt you can check your readme.txt at the readme.txt validator.

Now your're ready to review the Plugin Directory information zip your plugin up and submit it to the WordPress Repository.

Once your Plugin has been Approved

WordPress uses SVN, so you'll probably need something like TortoiseSVN to upload your plugin to the WordPress repository. More info on how to do that at this youtube video.

Here are some extra things that I didn't find out immediately that might be helpful to you.

Add images to your WordPress Plugin Page

You should have two images: "banner-772x250.jpg" and "icon-128x128.jpg". They can be in your plugin folder (the "trunk" of your WordPress repository), or it may be better to put them in your "assets" folder out of the way. These will be the banner image at the top of your plugin page and the square icon that people will see when they search for a plugin.

Add Screenshots

To add a screenshots section you simply add "== Screenshots ==" to your readme.txt.

== Screenshots ==

Here are some screenshots...

1. Nice screenshot of my plugin.
2. Another screenshot of my plugin.

Each number in this section will be a different screenshot. Screenshot 1 will look for the file "screenshot-1.jpg", and screenshot 2 will look for "screenshot-2.jpg". Similar to the banner and icon, you'll either put these images in your trunk or in your assets folder. The text by each number on the readme.txt is the caption that will go under the image on the screenshot page.

Everything All Together

The complete files from this "How to write a WordPress Plugin" tutorial can be cloned or downloaded from GitHub page.