The Problem
When WordPress multisite is configured in subdirectory mode (e.g., example.com/site2/), the .htaccess rewrite rules can conflict with existing rules, custom post types, or other plugins, causing 404 errors on subsites.
Symptoms
- Subsites return 404 errors
- Subsite admin dashboard loads but frontend is broken
- Main site works but subsites do not
- Custom post type URLs return 404 on subsites
- Media files on subsites return 404
Real Error
https://example.com/site2/about/ -> 404 Not Found
https://example.com/site2/wp-admin/ -> Works fineThe Correct Multisite Subdirectory .htaccess
```apache RewriteEngine On RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] RewriteBase / RewriteRule ^index\.php$ - [L]
# Uploaded files RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [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] ```
How to Fix It
Fix 1: Replace .htaccess with Correct Rules
```bash # Backup existing .htaccess cp .htaccess .htaccess.backup
# Replace with WordPress multisite subdirectory rules # (see the correct rules above) ```
Fix 2: Regenerate .htaccess via WP-CLI
```bash # Flush rewrite rules for all sites wp rewrite flush --hard --network
# Or per site wp site list --field=url | while read url; do wp rewrite flush --hard --url=$url done ```
Fix 3: Fix Custom Post Type Permalinks on Subsites
// functions.php (network-activated mu-plugin)
function fix_multisite_cpt_rewrite($post_type, $args) {
// Ensure CPT rewrite rules work on subsites
$args['rewrite'] = array_merge((array)$args['rewrite'], [
'with_front' => false // Do not prepend site subdirectory
]);
return $args;
}Fix 4: Nginx Multisite Subdirectory Configuration
```nginx server { server_name example.com; root /var/www/html; index index.php;
# Multisite subdirectory rules location / { try_files $uri $uri/ /index.php?$args; }
# Handle subsite paths rewrite ^/([_0-9a-zA-Z-]+/)?wp-admin$ /$1wp-admin/ permanent;
location ~ ^/([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) { # Allow access to WP files }
location ~ ^/([_0-9a-zA-Z-]+/)?(.*\.php)$ { fastcgi_pass unix:/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$2; include fastcgi_params; }
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ { expires 30d; add_header Cache-Control "public, immutable"; } } ```
Fix 5: Verify Subsite Configuration
```bash # List all subsites wp site list
# Check a specific subsite's URL wp option get home --url=https://example.com/site2 wp option get siteurl --url=https://example.com/site2
# Both should return https://example.com/site2 ```
Fix 6: Fix Media URLs on Subsites
// mu-plugin for multisite media
add_filter('upload_dir', function($uploads) {
// Fix media path for subdirectory multisite
$uploads['url'] = str_replace('/wp-content/uploads', '/site2/wp-content/uploads', $uploads['url']);
return $uploads;
});