arrow_back
Back

WordPress server setup: .htaccess, file permissions, and HTTPS issues

Andrew Dorokhov Andrew Dorokhov schedule 2 min read
menu_book Table of Contents

The default .htaccess

This is the block WordPress writes itself when pretty permalinks are enabled. If permalinks return 404 after a migration, the first thing to check is that this block is present and that mod_rewrite is enabled.

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

Everything you add manually should go outside the BEGIN/END markers, since WordPress rewrites the block between them.

Redirecting to HTTPS

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

File permissions

The detailed reasoning is in open_in_new this StackOverflow answer . The short version:

chown www-data:www-data -R *          # Let Apache be the owner
find . -type d -exec chmod 755 {} \;  # Directory permissions rwxr-xr-x
find . -type f -exec chmod 644 {} \;  # File permissions rw-r--r--

Fixing the FTP credentials prompt

If WordPress asks for FTP credentials when installing or updating plugins, it means it could not determine that it can write to the filesystem directly. Add this to wp-config.php:

define( 'FS_METHOD', 'direct' );

Keep in mind that this only works if the web server user actually owns the files — see the permissions section above.

Getting rid of an unwanted HTTPS redirect

When a site is stuck in an HTTPS redirect (or an infinite redirect loop), check these three places in order:

  1. The site address in the wp_options table — fix it with a search-and-replace script.
  2. The .htaccess file, for a redirect rule like the one above.
  3. Plugins that force SSL. In my case the culprit was Really Simple SSL.

For replacing values across the database (URLs after a domain change, for example), open_in_new Search-Replace-DB handles serialized data correctly, which a plain SQL REPLACE() does not.

A related case is running behind CloudFlare’s Flexible SSL, which has its own open_in_new well-documented redirect loop .

code

Need Help with Development?

Happy to help — reach out via the contacts or go straight to my Upwork profile.

work View Upwork Profile arrow_forward
Next Article

WordPress emails: wp_mail, sender headers, and HTML messages

arrow_forward