featured article

Using an .env file for database and other credentials with WordPress & WooCommerce

This is part 2 of my article series 25+ Tutorials on How to boost the performance of your WooCommerce store. This article focuses on completing the initial project setup of your Wordpress & Woocommerce store with a .env file where all your credentials and configurations can be stored in one place.

I admit it, this doesn’t improve your stores performance at all. But the benefits of using a .env file are that you keep important credentials for databases, payment gateways, etc. out of your document root, out of your database and thus have a more secure setup, with a single configuration file.

An .env file should never be committed to your repository – you will need to add /.env to your .gitignore file – by managing important settings in an .env file you move these settings out of the database but rather make them a part of your projects code. That is a good thing, because it helps you to keep different environments in sync. Most likely, for different environments (development, staging, production) the .env file will look different. But it makes sure, that your not missing an important setting in the backend when developing locally or sending your code to the production server.

Step 1: Let’s create the .env file

.env
#--------------------------------------------------------------------------------------------------# # ENVIRONMENT SETTING # # It can be anything, but "development", "staging" and "production" are supported out of the box. # # Do not use "development" on production and viceversa, because it has effect on debug settings. # #--------------------------------------------------------------------------------------------------# WORDPRESS_ENV=development #--------------------------------------------------------------------------------------------------# # MANDATORY DATABASE SETTINGS # #--------------------------------------------------------------------------------------------------# DB_NAME="my-project" DB_USER="my-project" DB_PASSWORD="my-password" #--------------------------------------------------------------------------------------------------# # HOME PAGE URL # # Optional, but If not set current server name will be used, e.g. http://www.example.com # #--------------------------------------------------------------------------------------------------# WP_HOME="http://localhost:8080" #################################################################################################### # # # Most of the times you do NOT need to edit further. # # Below there's a cheat sheet of all the available settings, all are optional, feel free to delete # # unused settings when you are done with editing. # # Note that settings are commented (prefixed with #). Remove comment to be able to use a setting. # # Edit with care. Do nothing if in doubt. Double check before save. # # For help see http://codex.wordpress.org/Editing_wp-config.php # # # # Table of Contents: # # - OTHER DATABASE SETTINGS # # - MULTISITE # # - CACHE # # - DEBUG # # - OTHER PATHS & URLS # # - WP AUTO-UPDATES # # - POST & MEDIA SETTINGS # # - PERFORMANCE # # - COOKIE # # - SECURITY # # - FILESYSTEM # # - CRON # # - LANGUAGES # # - PROXY # # - MISCELLANEOUS # # # #################################################################################################### #--------------------------------------------------------------------------------------------------# # OTHER DATABASE SETTINGS # #--------------------------------------------------------------------------------------------------# #DB_HOST #DB_CHARSET #DB_COLLATE #DB_TABLE_PREFIX #DO_NOT_UPGRADE_GLOBAL_TABLES #WP_ALLOW_REPAIR #CUSTOM_USER_TABLE #CUSTOM_USER_META_TABLE #--------------------------------------------------------------------------------------------------# # MULTISITE # # As of WP 3.0, you have the ability to create a network of sites by using the multisite feature. # # See http://codex.wordpress.org/Create_A_Network # #--------------------------------------------------------------------------------------------------# #WP_ALLOW_MULTISITE=false #MULTISITE=false #MU_BASE=/ #ALLOW_SUBDIRECTORY_INSTALL=false #SUBDOMAIN_INSTALL=true #DOMAIN_CURRENT_SITE #PATH_CURRENT_SITE=/ #SITE_ID_CURRENT_SITE=1 #BLOG_ID_CURRENT_SITE=1 #NOBLOGREDIRECT={$WP_SITEURL} #UPLOADBLOGSDIR=blogs.dir #UPLOADS=files #BLOGUPLOADDIR=files #WPMU_ACCEL_REDIRECT=false #WPMU_SENDFILE=false #--------------------------------------------------------------------------------------------------# # CACHE # #--------------------------------------------------------------------------------------------------# #WP_CACHE #--------------------------------------------------------------------------------------------------# # DEBUG # # If you set WORDPRESS_ENV, default debug values are used based on that. # #--------------------------------------------------------------------------------------------------# #WP_DEBUG=true #WP_DEBUG_LOG=false #WP_DEBUG_DISPLAY=true #SAVEQUERIES=true #SCRIPT_DEBUG=true #ERRORLOGFILE=/log/error.log #DIEONDBERROR=true #--------------------------------------------------------------------------------------------------# # URLS & PATHS # # If WP_HOME above is set, all other urls are set properly in the great majority of the cases. # # For very custom installations you may need to set some of the following. Do nothing if in doubt. # #--------------------------------------------------------------------------------------------------# #WP_SITEURL #WP_CONTENT_DIR=./../wp-content #WP_CONTENT_URL={$WP_SITEURL}/wp-content #WP_PLUGIN_DIR={$WP_CONTENT_DIR}/plugins #WP_PLUGIN_URL={$WP_CONTENT_URL}/plugins #WPMU_PLUGIN_DIR={$WP_CONTENT_DIR}/mu-plugins #WPMU_PLUGIN_URL={$WP_CONTENT_URL}/mu-plugins #WP_TEMP_DIR=tmp #--------------------------------------------------------------------------------------------------# # WP AUTO-UPDATES # #--------------------------------------------------------------------------------------------------# #AUTOMATIC_UPDATER_DISABLED=false #WP_AUTO_UPDATE_CORE=false #CORE_UPGRADE_SKIP_NEW_BUNDLED=true #--------------------------------------------------------------------------------------------------# # POST & MEDIA SETTINGS # #--------------------------------------------------------------------------------------------------# #AUTOSAVE_INTERVAL=60 #EMPTY_TRASH_DAYS=30 #WP_POST_REVISIONS=true #MEDIA_TRASH=false #IMAGE_EDIT_OVERWRITE=true #--------------------------------------------------------------------------------------------------# # PERFORMANCE # #--------------------------------------------------------------------------------------------------# #COMPRESS_CSS=false #COMPRESS_SCRIPTS=false #CONCATENATE_SCRIPTS=false #ENFORCE_GZIP=false #--------------------------------------------------------------------------------------------------# # COOKIE # #--------------------------------------------------------------------------------------------------# #COOKIEHASH #PASS_COOKIE #LOGGED_IN_COOKIE #AUTH_COOKIE #SECURE_AUTH_COOKIE #USER_COOKIE #TEST_COOKIE #COOKIE_DOMAIN #COOKIEPATH #SITECOOKIEPATH #ADMIN_COOKIE_PATH #PLUGINS_COOKIE_PATH #--------------------------------------------------------------------------------------------------# # SECURITY # #--------------------------------------------------------------------------------------------------# #DISALLOW_FILE_MODS=false #DISALLOW_FILE_EDIT=false #DISALLOW_UNFILTERED_HTML=true #ALLOW_UNFILTERED_UPLOADS=false #FORCE_SSL_LOGIN=false #FORCE_SSL_ADMIN=false #WP_HTTP_BLOCK_EXTERNAL #WP_ACCESSIBLE_HOSTS #--------------------------------------------------------------------------------------------------# # FILESYSTEM & FTP # #--------------------------------------------------------------------------------------------------# #FS_CHMOD_DIR=0755 #FS_CHMOD_FILE=0644 #FS_METHOD #FS_TIMEOUT=30 #FS_CONNECT_TIMEOUT=30 #FTP_USER #FTP_PASS #FTP_HOST #FTP_SSL #FTP_SSH #FTP_BASE #FTP_CONTENT_DIR #FTP_PLUGIN_DIR #FTP_PUBKEY #FTP_PRIKEY #--------------------------------------------------------------------------------------------------# # CRON # #--------------------------------------------------------------------------------------------------# #ALTERNATE_WP_CRON=false #DISABLE_WP_CRON=false #WP_CRON_LOCK_TIMEOUT=60 #--------------------------------------------------------------------------------------------------# # LANGUAGES # # WPLANG is deprecated since WP 4, use only for backward compatibility issues # #--------------------------------------------------------------------------------------------------# #WPLANG=en_EN #WP_LANG_DIR={$WP_CONTENT_DIR}/tmp #--------------------------------------------------------------------------------------------------# # MEMORY # #--------------------------------------------------------------------------------------------------# #WP_MEMORY_LIMIT=64M #WP_MAX_MEMORY_LIMIT=256M #--------------------------------------------------------------------------------------------------# # PROXY # #--------------------------------------------------------------------------------------------------# #WP_PROXY_HOST #WP_PROXY_PORT #WP_PROXY_USERNAME #WP_PROXY_PASSWORD #WP_PROXY_BYPASS_HOSTS #WP_ACCESSIBLE_HOSTS #WP_HTTP_BLOCK_EXTERNAL #--------------------------------------------------------------------------------------------------# # MISCELLANEOUS # #--------------------------------------------------------------------------------------------------# #WP_MAIL_INTERVAL=300 #WP_DEFAULT_THEME=twentyfifteen

This file is taken from Jeff Behnke’s perfect example on GitHub. Save it into your project root folder and add it to your .gitginore file /.env in order to prevent it from being committed to your repo. It’s also important that you store it outside your document root folder so that these credentials are not accessible on your server. Once the file is in place, we want to adjust the values for DB_NAME, DB_USER, DB_PASSWORD and WP_HOME. Set their values to the ones you have defined in Part 1 of this article series.

In order to tell PHP about these new environment variables we will install the package vlucas/phpdotenv from Packagist

composer require vlucas/phpdotenv

Step 2: Modify the wp-config.php

Open our wordpress/wp-config.php file and edit the very beginning of the file. This part is crucial and essentially enables the use of classes installed with Composer from within WordPress.

wordpress/wp-config.php
<?php /** @desc this loads the composer autoload file */ require_once dirname( __DIR__ ) . '/vendor/autoload.php'; /** @desc this instantiates Dotenv and passes in our path to .env */ $dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__)); $dotenv->load();

Keep in mind, that our modified wordpress/wp-config.php needs to be added to the preserve-paths section of our composer.json as we did in Part 1 of this article series. If you don’t do that, the file would revert to it’s original contents when you run a composer update.

Step 3: Using environment variables in PHP

WordPress (and its plugins) make heavy use of global constants which are set with define()s. You can now set them with values from our $_ENV variable. This variable holds all the key/value pairs from our .env file. In your wp-config.php you can replace the plain define with this:

wordpress/wp-config.php
defined('WORDPRESS_ENV') or define('WORDPRESS_ENV', $_ENV['WORDPRESS_ENV']); defined('WP_HOME') or define('WP_HOME', $_ENV['WP_HOME']); defined('DB_NAME') or define('DB_NAME', $_ENV['DB_NAME']); defined('DB_USER') or define('DB_USER', $_ENV['DB_USER']); defined('DB_PASSWORD') or define('DB_PASSWORD', $_ENV['DB_PASSWORD']);

It’s a good practice to test constants for their existence before defining them – this way you prevent a PHP warning should a constant be re-defined somewhere else. This is especially good advice when you plan on releasing a plugin to the world and use constants in your code. If you don’t check for existence before defining it, your plugin users won’t be able to declare them in their wp-config.php file since that would cause the redefine error – except if you silence all errors.

So now you can for example configure plugin settings in your .env and wp-config.php. Take WooCommerce for example, to define that your websites’s front page shall be the main shop page:

.env
#--------------------------------------------------------------------------------------------------# # PLUGINS # #--------------------------------------------------------------------------------------------------# # WooCommerce SHOP_IS_ON_FRONT=true

then in wp-config.php add:

wordpress/wp-config.php
defined('SHOP_IS_ON_FRONT') or define('SHOP_IS_ON_FRONT', $_ENV['SHOP_IS_ON_FRONT']);

This changes the WooCommerce Homepage from http://example.com/shop/ to be on your front-page at http://example.com/ instead.

Step 4: Override Plugin Settings

Let’s say we want to configure transactional emails for our website. First we want a plugin, for example Mailgun Plugin for WordPress, so we install it with composer require wpackagist-plugin/mailgun.

Wordpress stores the settings in its wp_options table. Luckily for us WordPress comes with a pre_option() filter, which allows us to modify a specific options value when WordPress reads it like this:

wordpress/wp-content/themes/storefront-child/functions.php
/** @desc override Mailgun settings and return API Credentials from .env file */ if (is_plugin_active('mailgun/mailgun.php')) { add_filter('pre_option_mailgun', 'override_mailgun_settings'); function override_mailgun_settings() : array { return [ 'region' => $_ENV['MAILGUN_REGION'], 'useAPI' => '0', 'domain' => $_ENV['MAILGUN_DOMAIN'], 'apiKey' => '', 'username' => $_ENV['MAILGUN_USER'], 'password' => $_ENV['MAILGUN_PASS'], 'secure' => '1', 'sectype' => 'tls', 'track-clicks' => 'htmlonly', 'track-opens' => '1', 'from-address' => $_ENV['MAILGUN_FROM'], 'from-name' => $_ENV['MAILGUN_FROMNAME'], 'override-from' => '1', 'campaign-id' => '', ]; } }

The example above configures Mailgun SMTP from within my themes functions.php. In addition to the small function above we also add the few settings to the .env:

.env
MAILGUN_REGION=eu MAILGUN_DOMAIN=mg.example.com MAILGUN_USER=postmaster MAILGUN_PASS="myS3cretP4ss" MAILGUN_FROM="service@example.com" MAILGUN_FROMNAME="Customer Service"

Now you know how to override get_option calls. Plugins save either strings or serialised arrays in the wp_options table, so by unserializing that string you get the array that you want your custom override_plugin_settings() function to return. Tip: copy and paste some serialised string from your database here to turn it into an array.

Likewise you can override any other call to get_option('some-plugin-settings'); which allows us to basically configure plugins from within our projects’ code without the need of importing or making all these settings in the WordPress backend manually. If someone tries to modify a plugins configuration in the backend this change would usually be ignored.

The settings fields are sometimes even grayed out.

Alternative: set environment variables in your Nginx.conf

Another option to define environment variables is to define them as environment variables in your Nginx configuration. Inside the location ~ \.php$ block of your configuration file you can also set environment variables like this:

/etc/nginx/sites-available/my-website.com.conf
fastcgi_param WORDPRESS_ENV production; fastcgi_param WP_HOME https://my-website.com; fastcgi_param WP_SITEURL https://my-website.com/; fastcgi_param WPLANG en_US;

These can the be accessed in PHP with $_SERVER['WORDPRESS_ENV'];. Personally I prefer the .env file.

All the code discussed in this tutorial can be found in the following ZIP archive. Note that in order for the code to work as expected you need to rename the file from .env.dist to .env and edit your credentials.

Download Tutorial: Using an .env file for database and other credentials using-an-env-file-for-credentials.zip (30.8 MB)

Continue to the next tutorial Part 3 “Debugging WordPress in your development environment”.