WordPress file structure and wp-config.php

Know which folders belong to core, which belong to you, and the wp-config.php settings developers change.

  • Intermediate
  • 7 min read
  • Updated 17 Oct 2026

What you will learn

  • Navigate the WordPress files
  • Configure wp-config.php
  • Keep secrets and environment settings out of your code

Before you start

A WordPress install has hundreds of files, but only a few folders matter day to day. Knowing which files belong to core and which belong to you tells you what is safe to change, what to keep in version control, and what an update will overwrite.

The top level

Open your local site's root folder. You will see something like this:

Plain text
my-site/
├── wp-admin/
├── wp-content/
├── wp-includes/
├── index.php
├── wp-config.php
├── wp-load.php
├── wp-settings.php
├── wp-login.php
├── wp-cron.php
├── xmlrpc.php
└── .htaccess        (Apache only)

Three folders and a handful of files:

PathBelongs toPurpose
wp-admin/CoreThe admin screens and their PHP
wp-includes/CoreThe bulk of WordPress: classes, functions, APIs, core blocks and scripts
wp-content/YouThemes, plugins, uploads and other site-specific files
index.phpCoreThe front controller: every front-end request starts here
wp-config.phpYouDatabase credentials and site configuration
wp-settings.php, wp-load.phpCoreBootstrap files that load WordPress
.htaccessSharedRewrite rules for pretty permalinks on Apache

Everything marked core is replaced on update. Treat it as read-only.

wp-content: your folder

All your work lives inside wp-content:

Plain text
wp-content/
├── plugins/
│   └── myplugin/
│       └── myplugin.php
├── themes/
│   └── twentytwentyfive/
├── mu-plugins/      (optional, must-use plugins)
├── uploads/
│   └── 2026/10/
├── languages/       (translation files)
└── debug.log        (when WP_DEBUG_LOG is on)
  • plugins/ holds one folder per plugin.
  • themes/ holds one folder per theme. Only one is active at a time, sometimes with a parent theme.
  • mu-plugins/ holds must-use plugins. Every PHP file directly inside this folder runs on every request and cannot be deactivated from the admin.
  • uploads/ holds media, organised by year and month by default. It is content, not code: back it up, but keep it out of your code repository.
  • languages/ holds translation files that WordPress downloads.

In a project repository, track your own plugin and theme folders, and ignore core, uploads/ and third-party plugins you install with a dependency manager or WP-CLI.

Getting paths and URLs in code

Never hard-code paths such as /var/www/html/wp-content/plugins/myplugin. Sites move, and some move wp-content itself. WordPress gives you functions that always return the right value:

PHP
// wp-content/plugins/myplugin/myplugin.php
defined( 'ABSPATH' ) || exit;

define( 'MYPLUGIN_PATH', plugin_dir_path( __FILE__ ) );
define( 'MYPLUGIN_URL', plugin_dir_url( __FILE__ ) );

require_once MYPLUGIN_PATH . 'includes/post-types.php';

plugin_dir_path( __FILE__ ) returns the server path of the folder that contains the current file, with a trailing slash. plugin_dir_url( __FILE__ ) returns the matching URL, for loading CSS or images.

Other useful functions:

FunctionReturns
get_stylesheet_directory()Path to the active theme (the child theme, if one is used)
get_template_directory()Path to the parent theme
get_stylesheet_directory_uri()URL of the active theme
wp_upload_dir()An array with the uploads path and URL
home_url( '/about/' )A full URL on the site front end
admin_url( 'options-general.php' )A full URL in the admin

The constant ABSPATH holds the path to the WordPress root, with a trailing slash. Core defines it in wp-load.php before it loads wp-config.php.

wp-config.php

wp-config.php is a plain PHP file that WordPress loads early. It defines constants that configure the site. It is created during installation from wp-config-sample.php. A trimmed version:

PHP
<?php
// wp-config.php

// Database settings.
define( 'DB_NAME', 'myproject' );
define( 'DB_USER', 'myproject' );
define( 'DB_PASSWORD', 'a-long-random-password' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8mb4' );
define( 'DB_COLLATE', '' );

// Authentication keys and salts.
define( 'AUTH_KEY', 'put your unique phrase here' );
// ... seven more keys and salts ...

$table_prefix = 'wp_';

define( 'WP_DEBUG', false );

/* That's all, stop editing! Happy publishing. */

if ( ! defined( 'ABSPATH' ) ) {
    define( 'ABSPATH', __DIR__ . '/' );
}

require_once ABSPATH . 'wp-settings.php';

The parts you will meet:

  • Database constants tell WordPress how to connect. utf8mb4 supports all Unicode characters, including emoji in content.
  • Keys and salts are random strings that sign login cookies and nonces. Generate fresh ones for each site with the WordPress.org secret key service or wp config shuffle-salts. Changing them logs everyone out.
  • $table_prefix is put in front of every table name. Two installs can share a database by using different prefixes.
  • The final lines load wp-settings.php, which boots WordPress. Every custom setting must go above the "stop editing" comment.

Settings developers change

These constants are the ones you will use most:

PHP
// wp-config.php, above "That's all, stop editing!"

// Environment and debugging.
define( 'WP_ENVIRONMENT_TYPE', 'local' );
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

// Stop file edits from the admin.
define( 'DISALLOW_FILE_EDIT', true );

// Limit stored revisions per post.
define( 'WP_POST_REVISIONS', 10 );

// Raise the memory limit for WordPress.
define( 'WP_MEMORY_LIMIT', '256M' );

// Run WP-Cron from a real system cron instead of page visits.
define( 'DISABLE_WP_CRON', true );
ConstantWhy you would set it
DISALLOW_FILE_EDITRemoves the theme and plugin file editors from the admin, a basic hardening step
DISALLOW_FILE_MODSAlso blocks installing and updating plugins and themes from the admin, for sites deployed from version control
WP_POST_REVISIONSCaps revisions so the posts table does not grow without limit
WP_MEMORY_LIMITRaises the PHP memory WordPress asks for, within the server's limit
DISABLE_WP_CRONStops scheduled tasks running on page loads; you then trigger them from system cron
WP_HOME, WP_SITEURLForce the site URLs, which overrides the values in the database

You will meet several of these again in the hardening, cron and deployment lessons.

Keep secrets out of version control

wp-config.php contains passwords and keys, and they differ per environment. Do not commit the real file. A common pattern is to read values from environment variables, which the server or your local tool sets:

PHP
// wp-config.php
define( 'DB_NAME', getenv( 'DB_NAME' ) ?: 'myproject' );
define( 'DB_USER', getenv( 'DB_USER' ) ?: 'root' );
define( 'DB_PASSWORD', getenv( 'DB_PASSWORD' ) ?: '' );
define( 'DB_HOST', getenv( 'DB_HOST' ) ?: 'localhost' );
define( 'WP_ENVIRONMENT_TYPE', getenv( 'WP_ENVIRONMENT_TYPE' ) ?: 'production' );

The ?: operator falls back to a default when the variable is not set. Production defaults to production, so a missing variable never switches on development behaviour.

wp-config.php must never be readable from the web as plain text. PHP files are executed rather than shown, but backup copies such as wp-config.php.bak are served as text. Never leave copies in the web root.

Summary

  • wp-admin, wp-includes and the root PHP files are core: read them, never change them.
  • wp-content is yours: plugins, themes, must-use plugins, uploads and translations.
  • Build paths and URLs with plugin_dir_path(), plugin_dir_url(), home_url() and similar functions.
  • wp-config.php holds database credentials, keys, the table prefix and configuration constants.
  • Put custom settings above the "stop editing" line and keep secrets out of version control.

In the next lesson you will follow a request from index.php all the way to the rendered page.

Further reading