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:
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:
| Path | Belongs to | Purpose |
|---|---|---|
wp-admin/ | Core | The admin screens and their PHP |
wp-includes/ | Core | The bulk of WordPress: classes, functions, APIs, core blocks and scripts |
wp-content/ | You | Themes, plugins, uploads and other site-specific files |
index.php | Core | The front controller: every front-end request starts here |
wp-config.php | You | Database credentials and site configuration |
wp-settings.php, wp-load.php | Core | Bootstrap files that load WordPress |
.htaccess | Shared | Rewrite 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:
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:
// 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:
| Function | Returns |
|---|---|
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
// 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.
utf8mb4supports 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_prefixis 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:
// 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 );
| Constant | Why you would set it |
|---|---|
DISALLOW_FILE_EDIT | Removes the theme and plugin file editors from the admin, a basic hardening step |
DISALLOW_FILE_MODS | Also blocks installing and updating plugins and themes from the admin, for sites deployed from version control |
WP_POST_REVISIONS | Caps revisions so the posts table does not grow without limit |
WP_MEMORY_LIMIT | Raises the PHP memory WordPress asks for, within the server's limit |
DISABLE_WP_CRON | Stops scheduled tasks running on page loads; you then trigger them from system cron |
WP_HOME, WP_SITEURL | Force 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:
// 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-includesand the root PHP files are core: read them, never change them.wp-contentis 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.phpholds 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.