Structuring your next WordPress project

WordPress

Setting up WordPress is famously easy! It takes only 5 minutes but it’s worth taking some time to look at the best way to structure your next WordPress project.

I think I should start by saying I’m not a fan of the way WordPress manages and lays out files, because by default it assumes the entire system should be simply dumped in public_html and things added to its sub folders. This makes automating deployments a bit of a pain and it also doesn’t really fit the way my brain likes to structure projects. Luckily it’s fairly easy to customise and move certain components.

This article is not a step by step “How to set up WordPress”, though it certainly has step by step components. Instead, its more a “How I like to structure projects”, and as such it’s opinionated both in terms of naming conventions and locations. So take what I do and borrow the bits you like and get rid of the bits you don’t.

Separating the application from custom code

WordPress consists of a “Core” application and then themes, plugins, and dropins.
By default themes and plugins are placed in a folder within the core application, so by separating out our “custom” code (so our plugins, themes etc) we can separate the common application from our custom code.

In effect we create something like this:

public_html
|-> core
|->custom
|-->plugins
|-->themes

So we now have 2 folders in the public_html the core which contains WordPress and a custom which has our plugins and theme. In the perfect world, the entire “application” (so all the PHP files) would sit below the public_html with only static assets, such as images, css and javascript, sitting inside public_html. Unfortunately without a substantial rewrite of WordPress core, and most plugins, this isn’t really feasible so we will have to settle for simply separating plugins/themes/uploads from WordPress.

To get this setup we do need to tell WordPress where we have hidden our plugins and themes so we need to add a wp-config file. However as WordPress looks in both the folder it’s in and the folder above we will place this file in the public_html folder. For now this wp-config.php will be pretty much the same as normal but with addition of a couple of lines in this case

define( 'WP_CONTENT_DIR', dirname( __FILE__ ) . '/custom' );
define( 'WP_CONTENT_URL', 'http://' . $_SERVER['HTTP_HOST'] . '/custom' );

This means all the folders that would have been stored under wp-content should now be found in /custom. This doesn’t just mean plugins and themes but also things like mu-plugins. It’s also worth noting that dropins should be placed directly in the /custom folder.

We also need to add an index.php to the public_html to bootstrap it all together, in this case simply copying the index.php in the core folder and putting it in public_html and adjusting the require to be:

// WordPress view bootstrapper
define( 'WP_USE_THEMES', true );
require( './core/wp-blog-header.php' );

So now we have:

public_html
|->index.php
|->wp-config.php
|-> core
|->custom
|-->plugins
|-->themes

When you install WordPress you will want to put the site address as your public_html root while the WordPress URL would be the /core. So for example:

http://example.com
http://example.com/core/

You would get to WP-Admin by going to
http://example.com/core/wp-admin/

Getting intimate with wp-config

We have already made an edit to wp-config.php but it’s worth taking a few minutes to look over this humble file.

As we have already seen, WordPress looks for the file in the root of the core folder first and then looks one directory up.

The file itself contains a bunch of config options in the form of defines, and it’s worth remembering that wp-config is a php file so you can include additional logic in there, for example you can include additional files, or add logic depending on circumstances. The basic file also contains only required defines and just within WordPress core there are dozens of “options” that can be defined in the file. Plugins may also suggest you add defines in there too.

Separating Routing and Passwords

This is a bit controversial but I split my wp-config.php into at least two parts, taking the passwords, salts and debug information and putting them in a separate file, one level down, which puts them outside of my public_html folder, leaving just routing information and an include to the password file in the wp-config.php.

There are a couple of reasons for doing this, the biggest single reason is it means local deployment still has the same routing, but can maintain an independent user and salt information. When managing version control these local config files are not versioned and are maintained separately. Also it means debug info can be on by default on your dev version but off on production.

The reason for dropping the password config file below the public_html is in the very rare chance of the server spitting out unprocessed php code, the sites passwords can’t directly be accessed. That said if something like that is occurring, those passwords should be changed as a matter of course and you got some pretty serious problems. Ultimately you need to weigh up the overhead of the extra include vs slightly cleaner and potentially more secure setup.

So now our application looks more like:

example.com/
|->local-config.php
|->public_html
|-->index.php
|-->wp-config.php
|--> core
|-->custom
|--->plugins
|--->themes

Separating theme assets from code

By default Javascript, CSS and images for the theme are included in the themes own folder which is fine but I prefer a separation of theme code and it’s assets, also by using a separate folder. This folder can be used directly with a CDN to deliver the content. Normally I place this folder in the public_html and simply call it assets/ with separate sub-directories for css and javascript as well as images.

example.com/
|->local-config.php
|->public_html
|-->index.php
|-->wp-config.php
|--> core
|-->custom
|--->plugins
|--->themes
|-->assets
|--->images
|--->css
|--->js

The contents of this folder is normally auto-generated using grunt or similar, as it’s normally combination of minified and compiled css and js. This folder and its subfolders can then be pulled by your CDN if you are using one with ease.

Uploads

User generated uploads are one of those things that as an admin I would happily ban, but unfortunately clients tend to want to include image in posts. As such I tend to use custom code which modifies the media manager to direct it to drop items in tmp and sends it to a queue using Gearman. A separate BASH script then does some basic checks before moving it to a subfolder in assets folder. This means the entire of the assets folder is set to make sure its not writable by the web server user.

However you can simply move uploads folder by using:

define( 'UPLOADS', 'assets/uploads' );

Remember, if you don’t set up a specific upload location, the media manager will obey the WP-CONTENT defines we included earlier. So in our setup the default location for uploads would be custom/uploads/

Scripts, logs and other bits

Most sites have one or more script that is going to be running, be it queue processing, batching, backups etc. These are nearly all run headless and separate to WordPress or through WP-CLI such scripts. To keep this together I tend to put them in a shell folder below the public_html folder, and to keep everything tidy I use a generic var folder for all these additional folders. In a similar vein I tend to keep my log files with my application so create a var/log while most exceptions will be going into Airbrake (well in my instance a CodebaseHQ Exceptions) I will normally have a range of log files. Non WordPress config files, where appropriate, get popped in var/conf.

Really Abstracting Config Files

WordPress wp-config.php is more then a config file, as a PHP file it can contain almost any code. For example I tend to put API Keys in as defines where possible to avoid them ending up in wp-options table. One idea that I have experimented with was creating dedicated config files using both YAML and .ini files.

The idea was that individual config sections would get their own files (so database.yml or database.ini keys.yml etc), these would be placed in the /var/config folder, and then called by the wp-config file.

I haven’t yet put this style setup on a live server, mainly because of the large overhead of introducing a YAML parser for yml files, though wp-cli makes use of yml files. While the overhead is less for using .ini files as there is a built in function within php. However, while I use .ini files day in day out, for some reason I don’t feel they are suitable though this is just a personal feeling.

The second reason I haven’t gone down this route is that WordPress is currently not accessing /var/ folder, so while PHP or HHVM (in some cases) might be running via supervisorD or cronjob this is normally the CLI version. So the var/ folder is not readable or writable normally by the http user.

That can easily be overcome by moving the conf folder or creating a second one at the root level. Ultimately I’m not sure if the abstraction is worth doing, maybe something for another blog post!

Backing Up

The final folder inside var is normally backup. As most of the files barring user uploaded material are stored in version control (see below) I tend to just backup by doing a mySQL dump of the database tables into a folder in var imaginatively called var/backup. This then rsyncs with my remote backup system. User uploads are handled separately.

So the final structure looks something like

example.com/
|->local-config.php
|->public_html
|-->index.php
|-->wp-config.php
|--> core
|-->custom
|--->plugins
|--->themes
|-->assets
|--->images
|--->css
|--->js
|--->uploads
|->var
|-->log
|-->conf
|-->shell
|-->backup

Putting it in version control

There are several ways you can maintain this sort of structure in GIT or your version control of choice. The first is to put the whole lot in a single repo and just git exclude the local-config.php, var folder and assets/upload folder. However I prefer to do a bit more separation to make everything a bit more reusable and create a single repo for WordPress core which just has the WordPress core code in it. This is then added as a GIT sub module. When you are ready to deploy a new version of WordPress it can be deployed to all your sites simultaneously.
Pro Tip, make sure your deployment tool copes with sub modules, or this will fail spectacularly

This means you are no longer maintaining multiple versions of WordPress, and makes updating when new releases come out a doddle. It also allows you to have different “streams” so your dev sites can work on a bleeding edge release of WordPress while older clients with more complex compliance issues can use an older version.

Getting going

The above has lots of opinionated choices about naming conventions and locations but I think its a good starting place for most projects. A slightly simpler skeleton was done by Mark Jaquith (and can be grabbed from GitHub). Indeed, his skeleton was the basis for a lot of the above.

The result of the above is a robust(ish) application setup which is easy to deploy and backup but also simple to manage.

Photo of folders: https://www.flickr.com/photos/odoublesnap/5040478919/

If you liked this post why not subscribe to my Newsletter (the signup is in the left hand sidebar). I promise not to spam you, I just send out an occasional email with exclusive content as well as a round-up of posts from TimNash.co.uk.

Helping you and your customers stay safe


WordPress Security Consulting Services

Power Hour Consulting

Want to get expert advice on your site's security? Whether you're dealing with a hacked site or looking to future-proof your security, Tim will provide personalised guidance and answer any questions you may have. A power hour call is an ideal starting place for a project or a way to break deadlocks in complex problems.

Learn more

Site Reviews

Want to feel confident about your site's security and performance? A website review from Tim has got you covered. Using a powerful combination of automated and manual testing to analyse your site for any potential vulnerabilities or performance issues. With a comprehensive report and, importantly, recommendations for each action required.

Learn more

Code Reviews

Is your plugin or theme code secure and performing at its best? Tim provides a comprehensive code review, that combine the power of manual and automated testing, as well as a line-by-line analysis of your code base. With actionable insights, to help you optimise your code's security and performance.

Learn more

Or let's chat about your security?

Book a FREE 20 minute call with me to see how you can improve your WordPress Security.

(No Strings Attached, honest!)