What Is WordPress?
WordPress is a content management system (CMS) based traditionally on the LAMP stack (Linux, Apache, MySQL, PHP). Nginx can, and often is, used instead of Apache – WordPress is web server agnostic. Similarly, MariaDB can be used instead of MySQL.
Everything in WordPress is a post, kind of like everything in Unix is a file. Posts are posts, pages are posts, images are posts. The only thing differentiating them is their post_type. All posts are stored in the wp_posts table in the database.
WordPress uses an event-driven architecture. Its core philosophy is its hook-based system, which allows for extending the behavior of the website without modifying any of the core files. It’s the whole reason why WordPress is so popular (because of the vast amount of third-party integrations allowed by its architecture).
It uses the Front Controller Pattern. All the requests (to non-static resources) are routed to the index.php file in the root directory. This file then “boots up” the entire WordPress machine.
WordPress File Structure
The structure outlined here strives to show some of the most important directories and files in WordPress. This knowledge will be useful for understanding the rest of this document. The list is by no means exhaustive.
- /
- .htaccess
- index.php
- wp-config.php
- wp-load.php
- wp-login.php
- wp-admin/
- wp-includes/
- wp-content/
- themes/
- your-block-theme/
- functions.php
- style.css
- theme.json
- templates/
- index.html
- parts/
- header.html
- footer.html
- your-classic-theme/
- functions.php
- index.php
- style.css
- your-block-theme/
- plugins/
- mu-plugins/
- uploads/
- 2025/
- 07/
- image.jpg
- 07/
- 2025/
- languages/
- themes/
You have to remember one important rule. Never, under any circumstances, modify any files inside the wp-admin or wp-includes directories. All of your changes will be overwritten on the next WordPress update. Your playground is the wp-content directory.
WordPress Database Overview
This section is just a brief, high-level overview of the WordPress database schema. It’s useful to gain some context about the database before we go further, but don’t stress about it. The details of how the database is actually used will be interwoven in relevant topics. The high-level bullet points in the list below signify tables. The low-level ones signify some (not all) of the most important columns in each table.
- wp_posts
- ID
- post_author
- post_date
- post_content
- post_title
- post_excerpt
- post_status
- post_modified
- post_parent
- post_type
- wp_postmeta
- meta_id
- post_id
- meta_key
- meta_value
- wp_terms
- term_id
- name
- slug
- wp_term_taxonomy
- term_taxonomy_id
- term_id
- taxonomy
- parent
- wp_term_relationships
- object_id
- term_taxonomy_id
- wp_termmeta
- meta_id
- term_id
- meta_key
- meta_value
- wp_users
- ID
- user_login
- user_pass
- user_nicename
- user_email
- display_name
- wp_usermeta
- umeta_id
- user_id
- meta_key
- meta_value
- wp_options
- option_id
- option_name
- option_value
- wp_comments
- comment_ID
- comment_post_ID
- comment_author
- comment_date
- comment_content
- wp_commentmeta
- meta_id
- comment_id
- meta_key
- meta_value
- wp_links
- link_id
- link_url
- link_name
You know the schema, now it’s time to understand it. The most important thing to remember about the database is that it’s designed primarily for flexibility. WordPress has to work with thousands of third-party plugins and themes. It’s the top priority that it doesn’t limit the possibilities of extending functionalities with an unnecessarily constraining design.
The “wp_” prefix of every table can and should be modified during installation (for security reasons). You should almost never interact with the database directly. There are a gazillion core functions in WordPress that handle database interactions for you, either directly or indirectly. Some very direct examples are wp_insert_post(), wp_update_post_meta(), or get_post(). If you ever find yourself in need of custom SQL (e.g., if you use custom tables), you should use the global $wpdb object.
wp_posts is the heart of the database. Every post is stored in this table. The column that makes the “everything is a post” methodology possible is post_type. It stores the type of the post, e.g., post, page, attachment, nav_menu_item, etc.
wp_terms, wp_term_taxonomy, and wp_term_relationships are tables required for taxonomies to work. We’ll dive deep into taxonomies later in this guide. All you need to know right now is that taxonomies are things like categories and tags. They let you create relationships and structure your posts.
wp_users stores the details of all users registered on the website. Some of the data are: login, password (hashed), email, display name, etc.
All meta tables (wp_postmeta, wp_termmeta, wp_usermeta, wp_commentmeta) are responsible for storing metadata associated with a given entry via a foreign key. You can see that all of those tables have basically the same columns. If you wanted to store some additional data (metadata) for a post (e.g., a product price), you would store it in the wp_postmeta table.
wp_options is pretty self-explanatory. It stores all the options for your website. If you change anything in the Settings menus, it’ll most likely be stored in this table.
wp_comments stores comments left for posts.
wp_links is an obsolete table. It was used by the core Links Manager module, providing the blogroll functionality. It was basically just a set of links to other blogs. This functionality has been disabled by default since WordPress 3.5. The table is kept for backward compatibility.
Routing & Permalinks Structure
WordPress acknowledges 3 types of permalink structures:
- plain permalinks, e.g, http://example.com/?p=N, where N is the ID of the post,
- almost pretty permalinks, e.g, http://example.com/index.php/post-name/,
- pretty permalinks, e.g., http://example.com/post-name/
Pretty permalinks are naturally the most user-friendly and SEO-friendly. You can configure the type of permalink structure used in Settings -> Permalinks.
To be able to use pretty permalinks, you have to configure your web server accordingly. That means rewriting all requests (which do not match any static file or directory) to the index.php file. Here’s what a typical .htaccess file in the root directory looks like:
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]This file is generated automatically by WordPress if you change the permalink structure to one that requires it. The ‘plain’ and ‘almost pretty’ permalinks don’t require a redirect as they are naturally routed to the index.php file. If you delete the .htaccess file, it’ll be regenerated when the flush_rewrite_rules() function is called, such as when you visit the permalinks settings page.
The most important utility pages on WordPress do not require .htaccess, even if the pretty permalink structure is used. That’s because they are all implemented to be accessed directly. The login page is by default /wp-login.php. That’s just a php file. The admin dashboard’s path is /wp-admin/, which is the name of the directory in the root folder. The index.php file inside /wp-admin/, which is called by default when the path is a directory, loads the admin dashboard. That’s why you can see all the paths in the admin dashboard having a .php suffix (e.g., /wp-admin/plugins.php) – they are just php files.