Skip to content

WordPress in Magento 2

Setting up nginx

Assuming you are using php-fpm, with an upstream record named fastcgi_backend, you can add the following under your records fot the default index and pub locations.

location /wp/ {
  index index.html index.php;
  try_files $uri $uri/ /wp/index.php?q=$uri&args;

  location ~ \.php$ {
    try_files $uri =404;
    fastcgi_pass   fastcgi_backend;
    fastcgi_buffers 1024 4k;

    fastcgi_param  PHP_FLAG  "session.auto_start=off \n suhosin.session.cryptua=off";
    fastcgi_param  PHP_VALUE "memory_limit=756M \n max_execution_time=18000";
    fastcgi_read_timeout 600s;
    fastcgi_connect_timeout 600s;

    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;

Managing the WordPress version

One method of managing the version of WordPress is to require it via composer.

The 2 packages that are well supported are roots/wordpress and johnpbloch/wordpress-core

We are going to be using the package roots/wordpress as it doesn't manage its own version of the WordPress codebase, but rather references the dist and source urls from the official WordPress github, essentially doing the job of adding a composer file to the official codebase.

Install paths

As the /wp folder can be removed and re-added by composer during a version update, we can't expect code inside it to persist.

This means plugins will need a separate install path. We'll go forward with the intention of setting this path to be /wp-content

By requiring a fork of composer/installers like "roots/wordpress-core-installer", the root composer file can add the following extra setup:

  "extra": {
    "wordpress-install-dir": "wp",
    "installer-paths": {
      "wp-content/mu-plugins/{$name}/": [
      "wp-content/plugins/{$name}/": [
      "wp-content/themes/{$name}/": [

URI path

In order to keep certain WordPress functionality like previews and drafts, the WordPress path needs to be different to the root Magento path.

The suggested path is /content, so the privacy page would have the uri of /content/privacy-policy

This has the added benefit of not having the WordPress controller resolve to every Magento URL, slowing down the site.


The config file should be outside the wp folder, but will need to be symlinked inside for compatibility with fishpig.

Using a symbolic link as opposed to a hard link will mean all paths defined are relative to the original file.

Due to the way fishpig processes the config file, the constants need to be in single quotes (as they should be) as absolute strings, aka no variables or constants to resolve the value.

However, this is problematic for constants to differ between environments.

In order to deal with this, we'll use env variables in the wp-config file, and add a plugin after fishpigs config getData call to populate these from Magento's env file, where we'll be managing these values per environment.

The following connection can be added to the app/etc/env.php

return [
    'db' => [
        'connection' => [
            'wordpress' => [
                'host' => '',
                'dbname' => 'REPLACE_WITH_DB_NAME',
                'username' => 'REPLACE_WITH_DB_USER',
                'password' => 'REPLACE_WITH_DB_PASS',
                'model' => 'mysql4',
                'engine' => 'innodb',
                'initStatements' => 'SET NAMES utf8;',
                'active' => '1',
                'AUTH_KEY' => 'REPLACE_WITH_HASH',
                'SECURE_AUTH_KEY' => 'REPLACE_WITH_HASH',
                'LOGGED_IN_KEY' => 'REPLACE_WITH_HASH',
                'NONCE_KEY' => 'REPLACE_WITH_HASH',
                'AUTH_SALT' => 'REPLACE_WITH_HASH',
                'SECURE_AUTH_SALT' => 'REPLACE_WITH_HASH',
                'LOGGED_IN_SALT' => 'REPLACE_WITH_HASH',
                'NONCE_SALT' => 'REPLACE_WITH_HASH',
                'driver_options' => [
                    1014 => false
The following wp-config.php can be added to the root of the Magento project

 * The base configuration for WordPress
 * The wp-config.php creation script uses this file during the
 * installation. You don't have to use the web site, you can
 * copy this file to "wp-config.php" and fill in the values.
 * This file contains the following configurations:
 * * MySQL settings
 * * Secret keys
 * * Database table prefix
 * @link
 * @package WordPress

$env = require __DIR__ . '/app/etc/env.php';
$wpEnv = $env['db']['connection']['wordpress'];
$env = null;

define('WP_HOME', '/content');
define('CONTENT_DIR', '/wp-content');
define('WP_DEFAULT_THEME', 'fishpig');

/** The name of the database for WordPress */
define('DB_NAME', $wpEnv['dbname']);

/** MySQL database username */
define('DB_USER', $wpEnv['username']);

/** MySQL database password */
define('DB_PASSWORD', $wpEnv['password']);

/** MySQL hostname */
define('DB_HOST', $wpEnv['host']);

/** Database Charset to use in creating database tables. */
define('DB_CHARSET', 'utf8');

/** The Database Collate type. Don't change this if in doubt. */
define('DB_COLLATE', '');

 * Authentication Unique Keys and Salts.
 * Change these to different unique phrases!
 * You can generate these using the {@link secret-key service}
 * You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again.
 * @since 2.6.0
define('AUTH_KEY', $wpEnv['AUTH_KEY']);
define('LOGGED_IN_KEY', $wpEnv['LOGGED_IN_KEY']);
define('NONCE_KEY', $wpEnv['NONCE_KEY']);
define('AUTH_SALT', $wpEnv['AUTH_SALT']);
define('LOGGED_IN_SALT', $wpEnv['LOGGED_IN_SALT']);
define('NONCE_SALT', $wpEnv['NONCE_SALT']);


 * WordPress Database Table prefix.
 * You can have multiple installations in one database if you give each
 * a unique prefix. Only numbers, letters, and underscores please!
$table_prefix = 'wp_';

 * For developers: WordPress debugging mode.
 * Change this to true to enable the display of notices during development.
 * It is strongly recommended that plugin and theme developers use WP_DEBUG
 * in their development environments.
 * For information on other constants that can be used for debugging,
 * visit the documentation.
 * @link
define('WP_DEBUG', false);

/** Absolute path to the WordPress directory. */
if (! defined('ABSPATH')) {
    define('ABSPATH', __DIR__ . '/');

/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';
switch_theme( WP_DEFAULT_THEME );


The fishpig theme will need to be symlinked to the theme folder, see the symlink section below.


Free plugins

In order to require free WordPress plugins, an additional repository can be added to the root composer file.


You can then composer require the plugin with a version. Clicking on the version in the search results from wpackagist will display the reference the composer require will need, but all packages follow the format, "wpackagist-plugin/REF" with the ref coming from the uri in the listings.

One option for paid & custom plugins is to add these to our own versioned repos, with the addition of a new composer file for each.

As this would require quite a bit of maintenance, an easier option is to add the plugins to the project and symlink them together.

As the /wp-content/plugins folder is ignored due to it being composer controlled, paid and custom plugins will be added to /wp-content/local-plugins

Please see the following symlinks section.

The symlink commands will be added as composer scripts to fire on post composer install and update events. This means that the /wp can be removed and re-added by composer, and the symlink put in place afterwards.

  "scripts": {
      "ln -sfn $PWD/wp-config.php $PWD/wp/wp-config.php",
      "ln -sfn $PWD/vendor/fishpig/magento2-wordpress-integration/wptheme $PWD/wp-content/themes/fishpig",
      "ln -sfn $PWD/wp-content/local-plugins/* $PWD/wp-content/plugins"
      "ln -sfn $PWD/wp-config.php $PWD/wp/wp-config.php",
      "ln -sfn $PWD/vendor/fishpig/magento2-wordpress-integration/wptheme $PWD/wp-content/themes/fishpig",
      "ln -sfn $PWD/wp-content/local-plugins/* $PWD/wp-content/plugins"


With the above taken into account, the root .gitignore file should have the following