Applies to: WordPress 7.1.3 on Apache 2.4 (tested with WP-CLI 2.12.0 on Debian 13), LiteSpeed Cache 7.9.1
The default WordPress .htaccess file holds one block of rewrite rules that sends every request for a page that is not a real file or folder to index.php. That is what makes "pretty" permalinks such as /sample-post/ work on Apache and LiteSpeed. To restore it, replace everything between # BEGIN WordPress and # END WordPress with the block below, or open Settings > Permalinks in WordPress and click Save Changes so WordPress writes it again.
The default WordPress .htaccess block
This is exactly what WordPress 7.1.3 wrote to a new single-site install in our lab (Post name permalinks, WordPress in the web root):
# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
# Any changes to the directives between these markers will be overwritten.
<IfModule mod_rewrite.c>
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]
</IfModule>
# END WordPress
The WordPress developer documentation shows the same rewrite lines without the comment lines and without the <IfModule mod_rewrite.c> wrapper. The rules are identical. The wrapper only tells Apache to skip the block when the mod_rewrite module is not loaded, so we recommend keeping it.
What each line does:
RewriteEngine Onturns on mod_rewrite for this folder.RewriteRule .* - [E=HTTP_AUTHORIZATION:...]passes the HTTPAuthorizationheader on to PHP. WordPress added it in version 5.6 for application passwords, because servers that run PHP as CGI or FastCGI do not pass that header on their own.RewriteBase /is the URL path of the WordPress install.RewriteRule ^index\.php$ - [L]stops processing whenindex.phpitself is requested.- The two
RewriteCondlines check that the request is not an existing file (!-f) or folder (!-d), so images, CSS andwp-adminare served directly. RewriteRule . /index.php [L]hands every other request to WordPress, which works out which post or page to show.
Where the .htaccess file is
The file sits in the folder WordPress is installed in, next to wp-config.php, wp-admin and wp-content. On a cPanel account that is usually public_html for the main domain. The name starts with a dot, so it is hidden: in cPanel File Manager turn on hidden files first (see how to edit .htaccess in cPanel File Manager).
If WordPress runs in a subfolder, the file lives in that subfolder and the paths change. With the site address set to http://example.com/blog, WordPress wrote these two lines instead (the rest of the block was the same):
RewriteBase /blog/ RewriteRule . /blog/index.php [L]
If permalinks are set to Plain (?p=123 links), WordPress needs no rewrite rules. In our test it left only the marker and comment lines, with nothing between them.
How to restore the WordPress .htaccess file
Method 1: let WordPress write it (easiest)
- Log in to WordPress and open Settings > Permalinks.
- Do not change anything. Click Save Changes.
- If the file (or, when it does not exist, the folder) is writable, WordPress writes its block and shows "Permalink structure updated."
- If it is not writable, WordPress 7.1.3 shows "You should update your .htaccess file now." and, at the bottom of the page, an error that the file "is not writable" followed by a box with the rules. Copy them and use method 2.
Method 2: paste the block by hand
- Download or copy the current
.htaccessfirst, for example as.htaccess.bak, so you can go back. - Open
.htaccessin cPanel File Manager or over SFTP. If the file does not exist, create it in the WordPress folder. - Delete only the lines from
# BEGIN WordPressto# END WordPressand paste the default block above in their place. If WordPress is in a subfolder, changeRewriteBaseand the lastRewriteRuleto match. - Leave blocks written by other plugins (caching, security, redirects) and your own rules where they are.
- Save the file and open the home page and one post in a private browser window.
Method 3: WP-CLI
On a VPS or any host with SSH, run wp rewrite flush --hard in the WordPress folder. In our test this command did not create the file at first; it only printed "Warning: Regenerating a .htaccess file requires special configuration. See usage docs." As the WP-CLI documentation explains, you must first tell WP-CLI that mod_rewrite is loaded, with a wp-cli.yml file in the WordPress folder:
apache_modules: - mod_rewrite
Then wp rewrite flush --hard printed "Success: Rewrite rules flushed." and wrote the block shown above. One catch from our lab: when the web server user could not write to the file, WP-CLI still printed "Success" but left the file unchanged. Always open the file afterwards to check. According to the WP-CLI documentation, --hard works only on single-site installs.
Keep your own rules outside the markers
WordPress only manages the lines between its own markers. In our test we added Options -Indexes above the block and broke the last rewrite line inside it. After a flush, WordPress put its own rules back and kept our line untouched. So:
- Put custom rules (redirects, HTTPS, security headers) above
# BEGIN WordPress, so they run before WordPress hands requests toindex.php. - Never edit inside the WordPress markers: the next permalink save overwrites it.
- Plugins add their own marked blocks. LiteSpeed Cache 7.9.1, for example, writes
# BEGIN LSCACHEand# BEGIN NON_LSCACHEblocks at the top of the file when it is activated or its settings are saved, removes them when it is deactivated, and saves a copy of the old file as.htaccess.bkthe first time it changes it.
WordPress Multisite .htaccess
Multisite uses different rules, and WordPress does not write them for you: the code that saves .htaccess stops on a multisite install. Copy the rules that WordPress shows on the Tools > Network Setup screen when you create the network, or use the version for your network type from the WordPress documentation. The subfolder version for WordPress 3.5 and later is:
# BEGIN WordPress Multisite
# Using subfolder network type: https://wordpress.org/documentation/article/htaccess/#multisite
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
RewriteRule . index.php [L]
# END WordPress Multisite
For a subdomain network, the wp-admin rule becomes RewriteRule ^wp-admin$ wp-admin/ [R=301,L] and the two lines before the last become RewriteRule ^(wp-(content|admin|includes).*) $1 [L] and RewriteRule ^(.*\.php)$ $1 [L]. Networks created with WordPress 3.4 or older need the older rules with the ms-files.php line, also on the WordPress page.
When .htaccess changes do nothing
- Apache ignores the file: Apache reads
.htaccessonly whereAllowOverrideallows it, and its default isNone. Rewrite rules need at leastAllowOverride FileInfoplusOptions FollowSymLinks(orSymLinksIfOwnerMatch), according to the Apache documentation. On Debian 13 (Apache 2.4.68) the stock config setsAllowOverride Nonefor/var/www/and mod_rewrite is off until you runa2enmod rewriteand restart Apache. A quick test from the Apache documentation: add a nonsense word such asTestMeon its own line and reload the page. If you do not get an HTTP 500 error, Apache is not reading the file. Remove the word again afterwards. - LiteSpeed: LiteSpeed Web Server supports
.htaccess, according to LiteSpeed. OpenLiteSpeed reads only the rewrite rules and, according to its documentation, must be restarted after you change them. - Nginx: Nginx has no
.htaccessat all. WordPress on Nginx needstry_files $uri $uri/ /index.php?$args;in the server block instead. - 500 error right after an edit: a typo or a directive that is not allowed in
.htaccessmakes Apache return HTTP 500 for the whole site. The web server error log names the problem line. Put your backup copy back, then add your changes one at a time.
If you would rather not manage Apache settings yourself, our cPanel web hosting runs on LiteSpeed, which supports .htaccess files.
Related articles: what is .htaccess and how to edit it in cPanel, force HTTPS for WordPress with .htaccess, how to set up redirects in WordPress, leverage browser caching with .htaccess in WordPress.
Official documentation: WordPress: Apache HTTPD and .htaccess, wp rewrite flush, Apache AllowOverride.