WordPress Internals: Loading Sequence

WordPress Internals: Loading Sequence

Whenever I start learning something new I prefer to have a deep understanding of the process that goes on behind the scene of whatever I’m trying to learn.

From experience, I can tell you that this can be both good and bad, because you can often get stuck and feel like you’re never gonna get it. Ultimately, a deeper understanding is a good thing. It just takes a few tries before you get there 🙂

When I first made contact with WordPress, I was modifying existing themes or doing front-end customization through plugins.

The more I worked with WordPress and the more of an advanced user I became, the more I felt like I was missing something. That something was the deeper understanding of the process that WordPress goes through to show content to the end user.

So, here it is.

The WordPress Loading Sequence

These are the steps that WordPress goes through from when you type an URL into the browser and until it presents you with whatever content you requested. Unfortunately, as it goes through these steps for every single page view, sometimes it can be a little slow.

First, we’ll take a look at a top level view of the process, after that we’ll get into an extended view and we’ll finish off with an almost complete loading sequence. At the end you’ll also find a chart of the entire process.

WordPress Loading Sequence: Basic

  1. Create base configuration for WordPress
  2. Set up version variables
  3. Set initial default constants
  4. Check if maintenance mode is enabled
  5. Check if debug mode is enabled
  6. Load early functionality files
  7. Load the database class file and instantiate the global variable
  8. Set up the default filters and actions for most of the WordPress hooks
  9. Initialize multi-site if enabled
  10. Load the localization library
  11. Load most of the remaining WordPress functionality
  12. Load must-use plugins
  13. Load multi-site plugins
  14. Create initial taxonomies and post types
  15. Load normal plugins
  16. Load pluggable functions
  17. Instantiate global query, rewrite and widget objects
  18. Load the active theme
  19. Set up the current user
  20. Build the query to get the requested data
  21. Select the correct template from the hierarchy
  22. The result of the request is sent to the browser

WordPress Loading Sequence: Extended

As with most PHP applications, the loading sequence starts with a index.php file and so is the case with WordPress.

  1. It all starts in the WordPress installation root folder with /index.php
  2. Set up MySQL settings
  3. Set up authentication data
  4. Set up database table prefix
  5. Set up debugging mode
  6. Load functions needed to load WordPress
  7. Load constants and global variables file that can be overridden, generally in /wp-config.php which was loaded earlier in /index.php
  8. Load the Plugin API
  9. Set up version variables
  10. Set up initial default constants
  11. Check if maintenance mode is enabled
  12. Check if debug mode is enabled
  13. Set up advanced caching functionality
  14. Load early core utility and class files
  15. Load the database class file and instantiate the $wpdb global variable
  16. Connect to MySQL and select database
  17. Start the object cache
  18. Set up the default filters and actions for most of the WordPress hooks
  19. Initialize multi-site if enabled
  20. Check if only the basics are needed and stop most of WordPress from being loaded
  21. Load the localization library
  22. Do a final check to see if the site needs to be installed
  23. Load most of the remaining WordPress functionality
  24. Load multi-site specific files
  25. Load must-use plugins
  26. Load network activated plugins
  27. do_action( 'muplugins_loaded' )
  28. Create initial taxonomies and post types
  29. Set up the default theme directory root
  30. Load normal plugins
  31. Load pluggable functions which you can easily overwrite from your themes or plugins
  32. do_action( 'plugins_loaded' )
  33. Instantiate global objects WP_Query, WP_Rewrite, WP and WP_Widget_Factory
  34. do_action( 'setup_theme' ) fires before the active theme is loaded
  35. Load the default text localization domain
  36. Load the functions.php file for the active theme
  37. do_action( 'after_setup_theme' ) fires after the theme is loaded
  38. Set up the current user
  39. do_action( 'init' ). Many themes and plugins use this hook as it is safe for running code
  40. do_action( 'widgets_init' )
  41. do_action( 'wp_loaded' )
  42. Run wp() to get the requested data
  43. do_action( 'template_redirect' ) fires before determining which template to load
  44. Select the correct template from the hierarchy
  45. apply_filters( 'template_include' )
  46. do_action( 'shutdown' )
  47. The result of the request is sent to the browser

WordPress Loading Sequence: Detailed

Whenever you type an address in the browser bar you make a request to a web hosting server. From there, the loading sequence of WordPress starts with the /index.php file in the root installation folder. Here is a detailed account of what is going on in the WordPress boot process.

1. A request is made

When you type in an address, the HTTP client, your browser, sends an HTTP request to a web server in the form of a request message.

2. /index.php

WordPress starts the loading sequence with this file. Two things are happening here:

  1. WP_USE_THEMES is defined which tells WordPress to load the active theme. If you are do not need to display the content stored in WordPress you can set this constant to false. Additionally you can display the WordPress content on a page from an existing website. To do this, use the following code on the page where you want to integrate WordPress:
    <?php define('WP_USE_THEMES'false); require('./wp-blog-header.php'); ?>
  2. Require /wp-blog-header.php which loads the WordPress environment

If you are requesting an admin page the process starts with the /wp-admin/index.php file.

3. /wp-blog-header.php

There are three things happening here:

  1. Checks if initial HTTP headers have been sent via the $wp_did_header variable. This is done so that the functionality in this file is executed only once
  2. Require /wp-load.php which sets up the entire WordPress environment
  3. Execute wp() function from /wp-includes/functions.php file, which sets up and runs the WordPress query to get the requested data
  4. Require /wp-includes/template-loader.php which loads the correct template from the hierarchy, based on the requested URL

4. /wp-load.php

This is a bootstrap file which contains two main pieces of functionality:

  1. Define the ABSPATH constant as this file’s directory. This is the root directory of the WordPress installation
  2. Require /wp-config.php which contains the base configuration for the WordPress environment. If it doesn’t find such a file, the WordPress installation process will start.

5. /wp-config.php

This is what happens in this file:

  1. Set up MySQL settings
  2. Set up authentication data. This is done though a few constants which you can change at any point in time to invalidate all existing cookies. This will force all users to have to log in again
  3. Set up database $table_prefix
  4. Set up WordPress debugging mode by defining the WP_DEBUG constant. Defaults to false. You can set this to true to show PHP errors
  5. Require /wp-settings.php which sets up WordPress variables and includes other files

At this point, WordPress has all the information it needs to securely access the database and load the requested data.

6. /wp-settings.php

This is the most important file in the loading sequence as it sets up most of the WordPress functionality and includes other needed files. Here is what’s going on and the most important steps:

  1. Define the WP_INC constant which stores the location of the WordPress directory of functions, classes and core content. This points to /wp-includes in the root folder
  2. Require /wp-includes/load.php which contains functions used for further initialization and loading WordPress. The functions in this file will be used further down.
  3. Require /wp-includes/default-constants.php which defines constants and global vars that can be overridden in /wp-config.php
  4. Require /wp-includes/plugin.php which loads the Plugin API. The functionality from installed plugins is not yet available.
  5. Set up version variables: $wp_version, $wp_db_version, $tinymce_version, $required_php_version, $required_mysql_version, $wp_local_package
  6. Set up $blog_id global variable which defaults to 1 in single site configuration. In multi-site, it will be overridden by default in /wp-includes/ms-settings.php
  7. Set initial default constants: WP_MEMORY_LIMIT, WP_MAX_MEMORY_LIMIT, WP_DEBUG, SCRIPT_DEBUG, WP_CONTENT_DIR and WP_CACHE
  8. Check if maintenance mode is enabled. This is true if there is a file .maintenance in the root folder or if the WordPress installation mode is active
  9. Check if WP_DEBUG mode is enabled
  10. Set up advanced caching functionality
  11. Define the WP_LANG_DIR constant which sets up the location of the language directory
  12. Require /wp-includes/compat.php file adds functions  missing from older PHP versions
  13. Require /wp-includes/wp-class-list-util.php file which contains a utility class to handle operations on an array of objects
  14. Require /wp-includes/functions.php file which contains the main WordPress API and the most important functionality to run a website
  15. Require /wp-includes/class-wp-matchesmapregex.php file which contains a helper class related to string operations
  16. Require /wp-includes/class-wp.php file which contains the object for building the WordPress query. This object is instantiated further down
  17. Require /wp-includes/class-wp-error.php file which contains a class for handling errors in WordPress
  18. Require /wp-includes/pomo/mo.php file which contains a class for working with translation files
  19. Set up $wpdb global variable and load the database class file and object cache. This functionality provides database connections and caching. It also allows the use of drop-ins which can be used to substitute the default classes and functionality here.
  20. Require /wp-includes/default-filters.php file which sets up default actions and filters for most of the WordPress hooks
  21. Initialize multi-site if enabled. The multi site functionality can be changed by loading a /wp-content/sunrise.php drop-in file
  22. End the loading of basic functionality
  23. Check if only basic WordPress functionality is needed by checking the SHORTINIT constant. This constant can be used in themes and plugins to only load the very basics of WordPress.
  24. Load the Translation API
  25. Do a final check to see if the site needs to be installed
  26. Load most of the remaining WordPress functionality by requiring some more files
  27. Require multi-site specific files
  28. Define plugin directory WordPress constant: WP_CONTENT_URL, WP_PLUGIN_DIR, WP_PLUGIN_URL, PLUGINDIR, WPMU_PLUGIN_DIR, WPMU_PLUGIN_URL, MUPLUGINDIR
  29. Load must-use plugins
  30. Load network activated plugins
  31. do_action( 'muplugins_loaded' ) This is the earliest hook you can use. There are no normal plugins loaded at this point
  32. Require /wp-includes/vars.php file which creates common global vars for the rest of WordPress
  33. Create initial post types and taxonomies
  34. Load normal plugins
  35. Require /wp-includes/pluggable.php which loads functions that can be replaced via plugins or themes
  36. do_action( 'plugins_loaded' )
  37. Define constants that affect functionality: AUTOSAVE_INTERVAL, EMPTY_TRASH_DAYS, WP_POST_REVISIONS, WP_CRON_LOCK_TIMEOUT
  38. Instantiate the global objects WP_Query, WP_Rewrite, WP and WP_Widget_Factory which were loaded earlier
  39. do_action( 'setup_theme' ) fires before the active theme is loaded
  40. Define the template related constants: TEMPLATEPATH, STYLESHEETPATH, WP_DEFAULT_THEME
  41. Load default translated strings based on locale
  42. Load the functions for the active theme, for both parent and child theme if applicable. This is done by checking if functions.php file exists in STYLESHEETPATH and TEMPLATEPATH which were defined earlier in the wp_templating_constants() function from /wp-includes/default-constants.php
  43. do_action( 'after_setup_theme' ) fires after the theme is loaded
  44. set up the current user
  45. do_action( 'init' ) fires after WordPress has finished loading but before any headers are sent. Most of WP is loaded at this stage, and the user is authenticated. This hook can be safely used for running code. Plugins use this hook to instantiate themselves for reasons like user, taxonomy and post type availability
  46. do_action( 'wp_loaded' ) fires once WP, all plugins, and the theme are fully loaded and instantiated.

This is the end of the /wp-settings.php file and we continue in the /wp-blog-header.php file.

7. wp()

This function is located in the /wp-includes/functions.php file which was loaded earlier in the /wp-settings.php file as part of the early WordPress files. This function sets up the WordPress query by checking the requested URL in order to get the requested data.

8. /wp-includes/template-loader.php

WordPress has arrived at the final part of its loading process. All of the WordPress functions are available through the /wp-settings.php file and the requested data is loaded by the wp() function. It’s time to load the correct template from the WordPress template hierarchy. This is what’s going on:

  1. do_action( 'template_redirect' ) fires before determining which template to load. This hook can be used to load an alternative template if the loaded data doesn’t need the default one
  2. Check if the current request is a header request and if so, exit before the page content loads. This provides a significant performance bump.
  3. Check for 3 special kinds of requests: robots, feeds, trackbacks. If these are detected, a template file does not need to be loaded
  4. Go through a list of checks and determine the template file to be loaded. If the file exists it is stored in the $template variable, else the default index template is stored
  5. Filter the $template variable before including the template file

9. do_action( 'shutdown' )

This is the last action that is being fired, just before ending all PHP execution. WordPress has run all the code needed to build the requested page and it stops working at this point.

10. The result of the request is sent to the browser

At this point, the web hosting server replies to the HTTP client request by sending it the web page generated by WordPress and comprised of HTML, CSS and JavaScript code which tells the browser how to display the page.

There it is. The WordPress boot process in three flavors: basic, extended and detailed. I’ve also made a chart of the whole process which you can see below.

Please comment with your thoughts, things I may have missed or steps that you think are important and I have overlooked.

  1. Hello Mike,

    Thanks for providing such a detailed write up on WordPress loading process. Without prior reading up WordPress design spec, your post is by far the best explanation on this topic that I could find.

    I am relatively new to web technologies, have been reading and practicing a lot of web development related materials for the past 2 months. Understood that your explanation is already very detailed, but I am still having problem understanding certain steps on how WordPress sends a page back to the browser. Eg. assuming one creates/adds a blank page called “Contact Us” and includes it as part of the navigation menu, when this menu link is clicked, the browser would send an HTTP request via the URL http://WWW.example.com/contact-us. I knew there is no physical file called contact-us.html or contact-us.php, how does WordPress redirect/map/translate (short of knowing the right word for it) the URL such that it knows which page to generate based on the content/settings stored in the database ?

    If you don;t mind, please be as detailed as you could in your explanation as I highly appreciate the details in understanding something.

    Thanks in advance.

    1. Hi Pang,

      Upon installation, WordPress creates a ‘wp_posts’ table in its database where it stores the data related to the posts/pages you create.

      When running the ‘wp()’ function, that is step 7 in the detailed sequence above, it finds a page called ‘contact-us’ in the database table mentioned above and grabs all the relevant data it finds there.

      WordPress then continues with loading the correct template and so on.

  2. Hello Mike,
    Thanks for your reply.
    Following your guidance, I added some trace codes in my website php file to see how WordPress takes the URL request and maps it to a page stored in WP_Post in the database. As I started this investigation with the Contact-Us page, my code was tracing what happened to the PHP files when Contact-Us was clicked.
    Everything seemed to become much clearer, I saw that the URL was parsed in function parse_request in the WP() class, and the page was queried in get_posts in the WP_Query() class, until something messed up. Somehow, my website could no longer point to the correct Contact-Us page, it always pointed to the home page. I didn’t know when this started to happen, I tried to restore as many original PHP files as possible but it still didn’t get fixed.

    Here’s what I found:
    1. The .htaccess file was not changed.
    2. On other working pages, the URL would show localhost/wordpress/page, eg. if I clicked on “My account” page, the URL would show localhost/wordpress/my-account. However, clicking on “Contact us” page only showed localhost/wordpress/.
    3. Using the browser developer tool, I could see that the hyperlink to the Contact Us page seemed to be correctly shown as href=”http//localhost/wordpress/contact-us/”, like the other working pages.
    4. I added a line in index.php in the root folder to show $_SERVER[‘REQUEST_URI’] in the browser Console, while the working page would show something like /wordpress/my-account, the Contact Us page showed /wordpress/only, missing the contact-us page name.
    5. I checked wp_post in my SQL database for the website, it’s still there.

    I am Very sure the Contact Us page was working before, and it was still working as I traced through the PHP files, until some point in time when more trace codes were added, it stopped to show up and I am having difficulty to trace back what messed up.

    Based on what I described above, could you give me some advise on what could have gone wrong that resulted in the Contact Us page not getting through the URL request ?

    1. Hi Pang,

      Sorry you’re having troubles.

      The problem might come from a permalink saving error.
      Try going to Settings > Permalinks and just click ‘Save Changes’ at the bottom.

      This will refresh the permalink settings and hopefully fix the situation.

  3. Hello Mike,
    I had some further observation. I brought up the Network tab of the browser developer tool, and noticed that on working page I would see status 200 from loading the page, while on Contact Us page I got status 301, and the Size column of the Network tab (I was using Google Chrome) showed “From disk cache”.
    I don’t know what status 301 is, I did some reading, it said something about URL redirect, which I don’t fully understand how it’s done, but I tried clearing the browser cache via the Clear Browsing Data option, the problem was fixed, now the Contact Us page is loading correctly.

    Why is it so ? How did the problem start in the first place ? I would like to prevent it from happening as having to ask visitors to our website to clear their browser cache is an Unpleasant user experience.

    1. Hi Pang,

      I’m glad you solved the issue.

      Unfortunately, I can’t tell you how the issue started as there are many factors that could have triggered it.

      In order to find out the cause you have to do some testing and replicate some of your actions.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.