Why we switched from Wordpress to Node.js

By Darshan - 1/22/2020

This was not the post I planned to write. In fact, I had a draft I was about to publish - How to run Wordpress+SSL on AWS Lightsail SSL for Cheap.

But what happened instead was, after a couple weeks of configuring Wordpress, investing in some plugins, and tweaking the deployment process & caching system, I threw it all out the door - I abandoned Wordpress.

Why use Wordpress in the first place?

We initially chose Wordpress because we assumed it was easy. At my former company, I was often the person saying Why don't we just use Wordpress? And for good reason - having our engineering team spend time on content management and marketing pages just seemed like a waste. With Wordpress's robust ecosystem that had a lot of SEO and social media plugins, most of the needs of our marketing team were handled perfectly.

In the event something catastrophic happened, loading up the previous day's backup wasn't a big deal - there was minimal risk, minimal updating. So generally speaking, Wordpress was a common answer that was often effective.

So when it came down to picking a platform to run a card games site, I figured Wordpress would be a good fit. My friend and I launched Solitaired to replace another Solitaire game that my mother in law played. That game had very specific features, but she downloaded it years ago and it was no longer online (even unavailable via the Wayback Machine). The game itself was a regular on-page javascript file, but we wanted to be able to have a platform that was scalable (in case we got real users), has blogging support, and was easy to launch.

Therefore, a simple app + lots of content made it seem like Wordpress should be the way to go. I found Amazon Lightsail (AWS's Digital Ocean knockoff) for $3.50/mo and said here we go.

After a couple of weeks of plugging away, the Wordpress site was up. The stack:

  • Wordpress 5.3.1
  • Default TwentyTwenty theme
  • AWS Lightsail
  • AWS Cloudfront for CDN, SSL & caching

That's when things started to go awry.

Challenges with Wordpress

Here are some of the issues I faced:

SSL & SITE_URL drove me crazy.

If you want SSL for your site, you either need to install the SSL certificates right on the app server directly, or you do so on the load balancer or CDN in front of your app servers.

If you expect any amount of traffic, the general recommendation is the latter choice - you get the benefits of scale with load balancers and CDNs, as well as speed & lower server requirements if you have a full CDN in front.

Our setup was:

Route 53 aliased to Cloudfront ->
Cloudfront force upgrade to HTTPS (443) ->
App server via HTTP (port 80)

The problem was that Wordpress has a SITE_URL setting in its wp-config.php file. When I set it to https://solitaired.com, it would go into a big redirect loop. Basically, the app server thought we were on HTTP (since it's seeing traffic from port 80) whereas the user wants to go to HTTPS (due to the HTTPS upgrade from Cloudfront). I'll spare you the steps I took to figure this out, but here's the solution:

In wp-config.php:

  • Add $_SERVER['HTTPS']='on';
  • For your SITE_URL, use the http version. In our case, this meant we used http://solitaired.com

Local, staging, and production environment was difficult, if not impossible, to automate.

Wordpress has a funny way of combining configuration, code, and database data that also includes config settings into one inextricable mass. I don't know if there's a Better Way (tm) to do this, but have fun transferring all the settings and changes you make each time you make changes on local or staging or production.

Suggestions to make life slightly less painful:

  • Add your wp-config.php file to .gitignore
  • Or extract as many settings you can out of wp-config.php and put them in a config JSON or something, and load in separate JSONs in each environment.
  • VersionPress looks cool, but as of Jan 20, 2020 the project hasn't been updated since Oct 2019 and says it's still a Developer's Preview; or try Bedrock which seems like it has more modern tooling. I haven't used either of them.

Caching plugins are confusing

This is a screenshot of a settings page of Wordpress's most popular caching plugin, W3 Total Cache.

W3 Total Cache

That's a lot of settings! Once you configure it, have fun figuring out whether something isn't updating because you forgot to upload, your update didn't work, or one of many caching layers is in place. Or maybe you edited a file in production but you overwrote it when you synced your local code with SFTP.

I decided to not use one of these plugins - instead, I set up Cloudfront to use my Origin headers and used the following cache settings in Apache's config (in Bitnami's image it is located at /opt/bitnami/apps/wordpress/conf/httpd-app.conf):

<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access 1 year"
ExpiresByType image/jpeg "access 1 year"
ExpiresByType image/gif "access 1 year"
ExpiresByType image/png "access 1 year"
ExpiresByType text/css "access 1 year"
ExpiresByType application/pdf "access 1 year"
ExpiresByType text/x-javascript "access 1 year"
ExpiresByType application/x-shockwave-flash "access 1 year"
ExpiresByType image/x-icon "access 1 year"
ExpiresDefault "access 1 hour"
</IfModule>

Multiple ways to do the same thing.

Q: Why is XYZ not working?

A: I am not sure. Let's check the UI Plugin settings. Nope, everything seems good there. Let's SFTP in and look at wp-config.php. Looks fine. Maybe it's .htaccess? Let's look there - oh and let me check the other http conf settings while we're here. No, that's fine too. Ah, maybe it's easier to see in the wp_options tableā€¦

You get the point.

A new beginning

To be frank, all of these issues are battles that can be won for those of us with enough grit. But the tedium eventually overcame me and I spent 1.5 days rewriting the entire app in node.js. Now the stack is:

  • Node.js (using sequelize for ORM)
  • Elastic Beanstalk & CodePipeline for deployment. Push to a branch and get a clean deploy! Straightforward support for environment variables!
  • AWS Cloudfront for CDN, SSL & caching.
  • Content published via markdown files pushed to github.

As for the original goals - scalable (check), content management (semi-check), easy to update (check).

The biggest shortcoming is the main core of WP, which is the CMS, doesn't have an analog in Node. But at this point I'll take the simple tradeoff we made to post new pages in Node to being forced to scroll through endless UI config pages, SSH grepping, or database querying to figure out what's going on. The power of Wordpress also turned out to be my biggest frustration.

Not for everyone

If you're not worried about modifying your Wordpress app often, or only use WP for solely CMS functionality on one portion of your site, keep going. Wordpress powers 35% of the Web apparently, so maybe I'm just ranting. But the pain feels real.

Suggestions welcome.

Now, life isn't perfect. What still seems missing with Node:

  • Easy user management: is there an ACL-based, secure package like Devise for node? (Not just a middleware like passport.js)
  • Lightweight CMS system: To get this app working, all of the content (pages & posts) were converted to markdown files and then converted into HTML using showdown. We edit right in Github. But I want something a bit more robust if that's possible. I have some ideas that I want to tackle in a future post.